filter: create filter from contact list

This adds a method for creating filters from contact lists. We will be
using this for creating follow timelines.

Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
William Casarin 2024-08-30 07:11:14 -07:00
parent ea547a3497
commit dd60d03407
3 changed files with 76 additions and 1 deletions

View File

@ -37,6 +37,7 @@ impl fmt::Display for SubscriptionError {
pub enum Error {
SubscriptionError(SubscriptionError),
LoadFailed,
EmptyContactList,
Io(io::Error),
Nostr(enostr::Error),
Ndb(nostrdb::Error),
@ -53,6 +54,9 @@ impl fmt::Display for Error {
Self::LoadFailed => {
write!(f, "load failed")
}
Self::EmptyContactList => {
write!(f, "empty contact list")
}
Self::Nostr(e) => write!(f, "{e}"),
Self::Ndb(e) => write!(f, "{e}"),
Self::Image(e) => write!(f, "{e}"),

View File

@ -1,5 +1,6 @@
use crate::note::NoteRef;
use nostrdb::Filter;
use crate::{Error, Result};
use nostrdb::{Filter, FilterBuilder, Note};
pub fn should_since_optimize(limit: u64, num_notes: usize) -> bool {
// rough heuristic for bailing since optimization if we don't have enough notes
@ -30,3 +31,65 @@ pub fn default_limit() -> u64 {
pub fn default_remote_limit() -> u64 {
150
}
/// Create a filter from tags. This can be used to create a filter
/// from a contact list
pub fn filter_from_tags(note: &Note) -> Result<FilterBuilder> {
let mut filter = Filter::new();
let tags = note.tags();
let mut authors: Vec<&[u8; 32]> = Vec::with_capacity(tags.count() as usize);
let mut hashtags: Vec<&str> = vec![];
for tag in tags {
if tag.count() < 2 {
continue;
}
let t = if let Some(t) = tag.get_unchecked(0).variant().str() {
t
} else {
continue;
};
if t == "p" {
let author = if let Some(author) = tag.get_unchecked(1).variant().id() {
author
} else {
continue;
};
authors.push(author);
} else if t == "t" {
let hashtag = if let Some(hashtag) = tag.get_unchecked(1).variant().str() {
hashtag
} else {
continue;
};
hashtags.push(hashtag);
}
}
if authors.is_empty() && hashtags.is_empty() {
return Err(Error::EmptyContactList);
}
// if we hit these ooms, we need to expand filter buffer size
if !authors.is_empty() {
filter.start_authors_field()?;
for author in authors {
filter.add_id_element(author)?;
}
filter.end_field();
}
if !hashtags.is_empty() {
filter.start_tags_field('t')?;
for hashtag in hashtags {
filter.add_str_element(hashtag)?;
}
filter.end_field();
}
Ok(filter)
}

View File

@ -1,5 +1,6 @@
use crate::app::{get_unknown_note_ids, UnknownId};
use crate::error::Error;
use crate::filter;
use crate::note::NoteRef;
use crate::notecache::CachedNote;
use crate::{Damus, Result};
@ -285,6 +286,13 @@ pub struct Timeline {
}
impl Timeline {
/// Create a timeline from a contact list
pub fn follows(contact_list: &Note) -> Result<Self> {
Ok(Timeline::new(vec![filter::filter_from_tags(contact_list)?
.kinds([1])
.build()]))
}
pub fn new(filter: Vec<Filter>) -> Self {
let subscription: Option<Subscription> = None;
let notes = TimelineTab::new(ViewFilter::Notes);