Merge rewrite deck serialization, timeline cache, add algo timelines #712
William Casarin (19):
algos: introduce last_n_per_pubkey_from_tags
wip algo timelines
Initial token parser combinator
token_parser: unify parsing and serialization
token_serializer: introduce TokenWriter
token_parser: simplify AddColumnRoute serialization
tokens: add a more advanced tokens parser
tokens: add AccountsRoute token serializer
tokens: add PubkeySource and ListKinds token serializer
tokens: add TimelineRoute token serializer
tokens: initial Route token serializer
add tokenator crate
note_id: add hex helpers for root notes
tokens: add token serialization for AlgoTimeline
tokens: add token serialization for TimelineKind
tokens: switch over to using token serialization
Switch to unified timeline cache via TimelineKinds
hashtags: click hashtags to open them
This commit is contained in:
@@ -414,6 +414,12 @@ impl Accounts {
|
||||
.or_else(|| self.accounts.iter().find_map(|a| a.to_full()))
|
||||
}
|
||||
|
||||
/// Get the selected account's pubkey as bytes. Common operation so
|
||||
/// we make it a helper here.
|
||||
pub fn selected_account_pubkey_bytes(&self) -> Option<&[u8; 32]> {
|
||||
self.get_selected_account().map(|kp| kp.pubkey.bytes())
|
||||
}
|
||||
|
||||
pub fn get_selected_account(&self) -> Option<&UserAccount> {
|
||||
if let Some(account_index) = self.currently_selected_account {
|
||||
if let Some(account) = self.get_account(account_index) {
|
||||
|
||||
@@ -35,6 +35,9 @@ impl From<String> for Error {
|
||||
pub enum FilterError {
|
||||
#[error("empty contact list")]
|
||||
EmptyContactList,
|
||||
|
||||
#[error("filter not ready")]
|
||||
FilterNotReady,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, thiserror::Error)]
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
use crate::error::{Error, FilterError};
|
||||
use crate::note::NoteRef;
|
||||
use crate::Result;
|
||||
use nostrdb::{Filter, FilterBuilder, Note, Subscription};
|
||||
use std::collections::HashMap;
|
||||
use tracing::{debug, warn};
|
||||
@@ -24,7 +23,7 @@ pub struct FilterStates {
|
||||
}
|
||||
|
||||
impl FilterStates {
|
||||
pub fn get(&mut self, relay: &str) -> &FilterState {
|
||||
pub fn get_mut(&mut self, relay: &str) -> &FilterState {
|
||||
// if our initial state is ready, then just use that
|
||||
if let FilterState::Ready(_) = self.initial_state {
|
||||
&self.initial_state
|
||||
@@ -190,13 +189,67 @@ impl FilteredTags {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a "last N notes per pubkey" query.
|
||||
pub fn last_n_per_pubkey_from_tags(
|
||||
note: &Note,
|
||||
kind: u64,
|
||||
notes_per_pubkey: u64,
|
||||
) -> Result<Vec<Filter>, Error> {
|
||||
let mut filters: Vec<Filter> = vec![];
|
||||
|
||||
for tag in note.tags() {
|
||||
// TODO: fix arbitrary MAX_FILTER limit in nostrdb
|
||||
if filters.len() == 15 {
|
||||
break;
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
let mut filter = Filter::new();
|
||||
filter.start_authors_field()?;
|
||||
filter.add_id_element(author)?;
|
||||
filter.end_field();
|
||||
filters.push(filter.kinds([kind]).limit(notes_per_pubkey).build());
|
||||
} else if t == "t" {
|
||||
let hashtag = if let Some(hashtag) = tag.get_unchecked(1).variant().str() {
|
||||
hashtag
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let mut filter = Filter::new();
|
||||
filter.start_tags_field('t')?;
|
||||
filter.add_str_element(hashtag)?;
|
||||
filter.end_field();
|
||||
filters.push(filter.kinds([kind]).limit(notes_per_pubkey).build());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(filters)
|
||||
}
|
||||
|
||||
/// Create a filter from tags. This can be used to create a filter
|
||||
/// from a contact list
|
||||
pub fn filter_from_tags(
|
||||
note: &Note,
|
||||
add_pubkey: Option<&[u8; 32]>,
|
||||
with_hashtags: bool,
|
||||
) -> Result<FilteredTags> {
|
||||
) -> Result<FilteredTags, Error> {
|
||||
let mut author_filter = Filter::new();
|
||||
let mut hashtag_filter = Filter::new();
|
||||
let mut author_res: Option<FilterBuilder> = None;
|
||||
@@ -284,3 +337,11 @@ pub fn filter_from_tags(
|
||||
hashtags: hashtag_res,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn make_filters_since(raw: &[Filter], since: u64) -> Vec<Filter> {
|
||||
let mut filters = Vec::with_capacity(raw.len());
|
||||
for builder in raw {
|
||||
filters.push(Filter::copy_from(builder).since(since).build());
|
||||
}
|
||||
filters
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ use enostr::NoteId;
|
||||
use nostrdb::{Ndb, Note, NoteKey, QueryResult, Transaction};
|
||||
use std::borrow::Borrow;
|
||||
use std::cmp::Ordering;
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
|
||||
pub struct NoteRef {
|
||||
@@ -10,9 +11,15 @@ pub struct NoteRef {
|
||||
pub created_at: u64,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Hash)]
|
||||
pub struct RootNoteIdBuf([u8; 32]);
|
||||
|
||||
impl fmt::Debug for RootNoteIdBuf {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "RootNoteIdBuf({})", self.hex())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)]
|
||||
pub struct RootNoteId<'a>(&'a [u8; 32]);
|
||||
|
||||
@@ -34,6 +41,10 @@ impl RootNoteIdBuf {
|
||||
root_note_id_from_selected_id(ndb, note_cache, txn, id).map(|rnid| Self(*rnid.bytes()))
|
||||
}
|
||||
|
||||
pub fn hex(&self) -> String {
|
||||
hex::encode(self.bytes())
|
||||
}
|
||||
|
||||
pub fn new_unsafe(id: [u8; 32]) -> Self {
|
||||
Self(id)
|
||||
}
|
||||
@@ -52,6 +63,10 @@ impl<'a> RootNoteId<'a> {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn hex(&self) -> String {
|
||||
hex::encode(self.bytes())
|
||||
}
|
||||
|
||||
pub fn to_owned(&self) -> RootNoteIdBuf {
|
||||
RootNoteIdBuf::new_unsafe(*self.bytes())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user