add TimelineUnits

Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
kernelkind
2025-08-24 23:39:51 -04:00
parent c1d6c0f535
commit 9b784dfdf7
2 changed files with 180 additions and 0 deletions

View File

@@ -30,6 +30,7 @@ pub mod kind;
mod note_units;
pub mod route;
pub mod thread;
mod timeline_units;
mod unit;
pub use cache::TimelineCache;

View File

@@ -0,0 +1,179 @@
use std::collections::HashSet;
use enostr::Pubkey;
use nostrdb::{Ndb, Note, NoteKey, Transaction};
use notedeck::NoteRef;
use crate::timeline::{
note_units::{InsertManyResponse, NoteUnits},
unit::{CompositeFragment, NoteUnit, NoteUnitFragment, Reaction, ReactionFragment},
};
#[derive(Debug, Default)]
pub struct TimelineUnits {
pub units: NoteUnits,
}
impl TimelineUnits {
pub fn with_capacity(cap: usize) -> Self {
Self {
units: NoteUnits::new_with_cap(cap, false),
}
}
pub fn from_refs_single(refs: Vec<NoteRef>) -> Self {
let mut entries = TimelineUnits::default();
refs.into_iter().for_each(|r| entries.merge_single_note(r));
entries
}
pub fn len(&self) -> usize {
self.units.len()
}
pub fn is_empty(&self) -> bool {
self.units.len() == 0
}
/// returns number of new entries merged
pub fn merge_new_notes<'a>(
&mut self,
payloads: Vec<&'a NotePayload>,
ndb: &Ndb,
txn: &Transaction,
) -> MergeResponse<'a> {
let mut unknown_pks = HashSet::with_capacity(payloads.len());
let new_fragments = payloads
.into_iter()
.filter_map(|p| to_fragment(p, ndb, txn))
.map(|f| {
if let Some(pk) = f.unknown_pk {
unknown_pks.insert(pk);
}
f.fragment
})
.collect();
let tl_response = if unknown_pks.is_empty() {
None
} else {
Some(UnknownPks { unknown_pks })
};
MergeResponse {
insertion_response: self.units.merge_fragments(new_fragments),
tl_response,
}
}
pub fn latest(&self) -> Option<&NoteRef> {
self.units.latest_ref()
}
pub fn merge_single_note(&mut self, note_ref: NoteRef) {
self.units.merge_single_unit(note_ref);
}
/// Used in the view
pub fn get(&self, index: usize) -> Option<&NoteUnit> {
self.units.kth(index)
}
}
pub struct MergeResponse<'a> {
pub insertion_response: InsertManyResponse,
pub tl_response: Option<UnknownPks<'a>>,
}
pub struct UnknownPks<'a> {
pub(crate) unknown_pks: HashSet<&'a [u8; 32]>,
}
pub struct NoteUnitFragmentResponse<'a> {
pub fragment: NoteUnitFragment,
pub unknown_pk: Option<&'a [u8; 32]>,
}
pub struct NotePayload<'a> {
pub note: Note<'a>,
pub key: NoteKey,
}
fn to_fragment<'a>(
payload: &'a NotePayload,
ndb: &Ndb,
txn: &Transaction,
) -> Option<NoteUnitFragmentResponse<'a>> {
match payload.note.kind() {
1 => Some(NoteUnitFragmentResponse {
fragment: NoteUnitFragment::Single(NoteRef {
key: payload.key,
created_at: payload.note.created_at(),
}),
unknown_pk: None,
}),
7 => to_reaction(payload, ndb, txn).map(|r| NoteUnitFragmentResponse {
fragment: NoteUnitFragment::Composite(CompositeFragment::Reaction(r.fragment)),
unknown_pk: Some(r.pk),
}),
_ => None,
}
}
fn to_reaction<'a>(
payload: &'a NotePayload,
ndb: &Ndb,
txn: &Transaction,
) -> Option<ReactionResponse<'a>> {
let reaction = payload.note.content();
let mut note_reacted_to = None;
for tag in payload.note.tags() {
if tag.count() < 2 {
continue;
}
let Some("e") = tag.get_str(0) else {
continue;
};
let Some(react_to_id) = tag.get_id(1) else {
continue;
};
note_reacted_to = Some(react_to_id);
break;
}
let reacted_to_noteid = note_reacted_to?;
let reaction_note_ref = NoteRef {
key: payload.key,
created_at: payload.note.created_at(),
};
let reacted_to_note = ndb.get_note_by_id(txn, reacted_to_noteid).ok()?;
let noteref_reacted_to = NoteRef {
key: reacted_to_note.key()?,
created_at: reacted_to_note.created_at(),
};
Some(ReactionResponse {
fragment: ReactionFragment {
noteref_reacted_to,
reaction_note_ref,
reaction: Reaction {
reaction: reaction.to_string(),
sender: Pubkey::new(*payload.note.pubkey()),
},
},
pk: payload.note.pubkey(),
})
}
pub struct ReactionResponse<'a> {
fragment: ReactionFragment,
pk: &'a [u8; 32],
}