add nip51 set caching structs
Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
@@ -16,6 +16,7 @@ mod jobs;
|
||||
pub mod media;
|
||||
mod muted;
|
||||
pub mod name;
|
||||
mod nip51_set;
|
||||
pub mod note;
|
||||
mod notecache;
|
||||
mod options;
|
||||
@@ -65,6 +66,7 @@ pub use media::{
|
||||
};
|
||||
pub use muted::{MuteFun, Muted};
|
||||
pub use name::NostrName;
|
||||
pub use nip51_set::{create_nip51_set, Nip51Set, Nip51SetCache};
|
||||
pub use note::{
|
||||
BroadcastContext, ContextSelection, NoteAction, NoteContext, NoteContextSelection, NoteRef,
|
||||
RootIdError, RootNoteId, RootNoteIdBuf, ScrollInfo, ZapAction,
|
||||
|
||||
195
crates/notedeck/src/nip51_set.rs
Normal file
195
crates/notedeck/src/nip51_set.rs
Normal file
@@ -0,0 +1,195 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use enostr::{Pubkey, RelayPool};
|
||||
use nostrdb::{Filter, Ndb, Note, Transaction};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{UnifiedSubscription, UnknownIds};
|
||||
|
||||
/// Keeps track of most recent NIP-51 sets
|
||||
#[derive(Debug)]
|
||||
pub struct Nip51SetCache {
|
||||
pub sub: UnifiedSubscription,
|
||||
cached_notes: HashMap<PackId, Nip51Set>,
|
||||
}
|
||||
|
||||
type PackId = String;
|
||||
|
||||
impl Nip51SetCache {
|
||||
pub fn new(
|
||||
pool: &mut RelayPool,
|
||||
ndb: &Ndb,
|
||||
txn: &Transaction,
|
||||
unknown_ids: &mut UnknownIds,
|
||||
nip51_set_filter: Vec<Filter>,
|
||||
) -> Option<Self> {
|
||||
let subid = Uuid::new_v4().to_string();
|
||||
let mut cached_notes = HashMap::default();
|
||||
|
||||
let notes: Option<Vec<Note>> = if let Ok(results) = ndb.query(txn, &nip51_set_filter, 500) {
|
||||
Some(results.into_iter().map(|r| r.note).collect())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(notes) = notes {
|
||||
add(notes, &mut cached_notes, ndb, txn, unknown_ids);
|
||||
}
|
||||
|
||||
let sub = match ndb.subscribe(&nip51_set_filter) {
|
||||
Ok(sub) => sub,
|
||||
Err(e) => {
|
||||
tracing::error!("Could not ndb subscribe: {e}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
pool.subscribe(subid.clone(), nip51_set_filter);
|
||||
|
||||
Some(Self {
|
||||
sub: UnifiedSubscription {
|
||||
local: sub,
|
||||
remote: subid,
|
||||
},
|
||||
cached_notes,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn poll_for_notes(&mut self, ndb: &Ndb, unknown_ids: &mut UnknownIds) {
|
||||
let new_notes = ndb.poll_for_notes(self.sub.local, 5);
|
||||
|
||||
if new_notes.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let txn = Transaction::new(ndb).expect("txn");
|
||||
let notes: Vec<Note> = new_notes
|
||||
.into_iter()
|
||||
.filter_map(|new_note_key| ndb.get_note_by_key(&txn, new_note_key).ok())
|
||||
.collect();
|
||||
|
||||
add(notes, &mut self.cached_notes, ndb, &txn, unknown_ids);
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl IntoIterator<Item = &Nip51Set> {
|
||||
self.cached_notes.values()
|
||||
}
|
||||
}
|
||||
|
||||
fn add(
|
||||
notes: Vec<Note>,
|
||||
cache: &mut HashMap<PackId, Nip51Set>,
|
||||
ndb: &Ndb,
|
||||
txn: &Transaction,
|
||||
unknown_ids: &mut UnknownIds,
|
||||
) {
|
||||
for note in notes {
|
||||
let Some(new_pack) = create_nip51_set(note) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
if let Some(cur_cached) = cache.get(&new_pack.identifier) {
|
||||
if new_pack.created_at <= cur_cached.created_at {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for pk in &new_pack.pks {
|
||||
unknown_ids.add_pubkey_if_missing(ndb, txn, pk);
|
||||
}
|
||||
|
||||
cache.insert(new_pack.identifier.clone(), new_pack);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_nip51_set(note: Note) -> Option<Nip51Set> {
|
||||
let mut identifier = None;
|
||||
let mut title = None;
|
||||
let mut image = None;
|
||||
let mut description = None;
|
||||
let mut pks = Vec::new();
|
||||
|
||||
for tag in note.tags() {
|
||||
if tag.count() < 2 {
|
||||
continue;
|
||||
}
|
||||
|
||||
let Some(first) = tag.get_str(0) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
match first {
|
||||
"p" => {
|
||||
let Some(pk) = tag.get_id(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
pks.push(Pubkey::new(*pk));
|
||||
}
|
||||
"d" => {
|
||||
let Some(id) = tag.get_str(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
identifier = Some(id.to_owned());
|
||||
}
|
||||
"image" => {
|
||||
let Some(cur_img) = tag.get_str(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
image = Some(cur_img.to_owned());
|
||||
}
|
||||
"title" => {
|
||||
let Some(cur_title) = tag.get_str(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
title = Some(cur_title.to_owned());
|
||||
}
|
||||
"description" => {
|
||||
let Some(cur_desc) = tag.get_str(1) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
description = Some(cur_desc.to_owned());
|
||||
}
|
||||
_ => {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let identifier = identifier?;
|
||||
|
||||
Some(Nip51Set {
|
||||
identifier,
|
||||
title,
|
||||
image,
|
||||
description,
|
||||
pks,
|
||||
created_at: note.created_at(),
|
||||
})
|
||||
}
|
||||
|
||||
/// NIP-51 Set. Read only (do not use for writing)
|
||||
pub struct Nip51Set {
|
||||
pub identifier: String, // 'd' tag
|
||||
pub title: Option<String>,
|
||||
pub image: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub pks: Vec<Pubkey>,
|
||||
created_at: u64,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Nip51Set {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Nip51Set")
|
||||
.field("identifier", &self.identifier)
|
||||
.field("title", &self.title)
|
||||
.field("image", &self.image)
|
||||
.field("description", &self.description)
|
||||
.field("pks", &self.pks.len())
|
||||
.field("created_at", &self.created_at)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user