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;
|
pub mod media;
|
||||||
mod muted;
|
mod muted;
|
||||||
pub mod name;
|
pub mod name;
|
||||||
|
mod nip51_set;
|
||||||
pub mod note;
|
pub mod note;
|
||||||
mod notecache;
|
mod notecache;
|
||||||
mod options;
|
mod options;
|
||||||
@@ -65,6 +66,7 @@ pub use media::{
|
|||||||
};
|
};
|
||||||
pub use muted::{MuteFun, Muted};
|
pub use muted::{MuteFun, Muted};
|
||||||
pub use name::NostrName;
|
pub use name::NostrName;
|
||||||
|
pub use nip51_set::{create_nip51_set, Nip51Set, Nip51SetCache};
|
||||||
pub use note::{
|
pub use note::{
|
||||||
BroadcastContext, ContextSelection, NoteAction, NoteContext, NoteContextSelection, NoteRef,
|
BroadcastContext, ContextSelection, NoteAction, NoteContext, NoteContextSelection, NoteRef,
|
||||||
RootIdError, RootNoteId, RootNoteIdBuf, ScrollInfo, ZapAction,
|
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