nip10: fetch unknown replied-to notes
Signed-off-by: William Casarin <jb55@jb55.com>
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -2432,7 +2432,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "nostrdb"
|
name = "nostrdb"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
source = "git+https://github.com/damus-io/nostrdb-rs?rev=99d8296fcba5957245ed883e2f3b1c0d1cb16397#99d8296fcba5957245ed883e2f3b1c0d1cb16397"
|
source = "git+https://github.com/damus-io/nostrdb-rs?rev=5733ece62b8495db8624a21637bacd12ebb22b2c#5733ece62b8495db8624a21637bacd12ebb22b2c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bindgen",
|
"bindgen",
|
||||||
"cc",
|
"cc",
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ serde_json = "1.0.89"
|
|||||||
env_logger = "0.10.0"
|
env_logger = "0.10.0"
|
||||||
puffin_egui = { version = "0.27.0", optional = true }
|
puffin_egui = { version = "0.27.0", optional = true }
|
||||||
puffin = { version = "0.19.0", optional = true }
|
puffin = { version = "0.19.0", optional = true }
|
||||||
nostrdb = { git = "https://github.com/damus-io/nostrdb-rs", rev = "99d8296fcba5957245ed883e2f3b1c0d1cb16397" }
|
nostrdb = { git = "https://github.com/damus-io/nostrdb-rs", rev = "5733ece62b8495db8624a21637bacd12ebb22b2c" }
|
||||||
#nostrdb = "0.3.3"
|
#nostrdb = "0.3.3"
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
base32 = "0.4.0"
|
base32 = "0.4.0"
|
||||||
|
|||||||
58
src/app.rs
58
src/app.rs
@@ -207,6 +207,7 @@ impl<'a> UnknownId<'a> {
|
|||||||
|
|
||||||
fn get_unknown_note_ids<'a>(
|
fn get_unknown_note_ids<'a>(
|
||||||
ndb: &Ndb,
|
ndb: &Ndb,
|
||||||
|
cached_note: &CachedNote,
|
||||||
txn: &'a Transaction,
|
txn: &'a Transaction,
|
||||||
note: &Note<'a>,
|
note: &Note<'a>,
|
||||||
note_key: NoteKey,
|
note_key: NoteKey,
|
||||||
@@ -218,6 +219,24 @@ fn get_unknown_note_ids<'a>(
|
|||||||
ids.insert(UnknownId::Pubkey(note.pubkey()));
|
ids.insert(UnknownId::Pubkey(note.pubkey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// pull notes that notes are replying to
|
||||||
|
if cached_note.reply.root.is_some() {
|
||||||
|
let note_reply = cached_note.reply.borrow(note.tags());
|
||||||
|
if let Some(root) = note_reply.root() {
|
||||||
|
if ndb.get_note_by_id(txn, root.id).is_err() {
|
||||||
|
ids.insert(UnknownId::Id(root.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !note_reply.is_reply_to_root() {
|
||||||
|
if let Some(reply) = note_reply.reply() {
|
||||||
|
if ndb.get_note_by_id(txn, reply.id).is_err() {
|
||||||
|
ids.insert(UnknownId::Id(reply.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let blocks = ndb.get_blocks_by_key(txn, note_key)?;
|
let blocks = ndb.get_blocks_by_key(txn, note_key)?;
|
||||||
for block in blocks.iter(note) {
|
for block in blocks.iter(note) {
|
||||||
if block.blocktype() != BlockType::MentionBech32 {
|
if block.blocktype() != BlockType::MentionBech32 {
|
||||||
@@ -288,8 +307,11 @@ fn poll_notes_for_timeline<'a>(
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|key| {
|
.map(|key| {
|
||||||
let note = damus.ndb.get_note_by_key(txn, *key).expect("no note??");
|
let note = damus.ndb.get_note_by_key(txn, *key).expect("no note??");
|
||||||
|
let cached_note = damus
|
||||||
let _ = get_unknown_note_ids(&damus.ndb, txn, ¬e, *key, ids);
|
.note_cache_mut()
|
||||||
|
.cached_note_or_insert(*key, ¬e)
|
||||||
|
.clone();
|
||||||
|
let _ = get_unknown_note_ids(&damus.ndb, &cached_note, txn, ¬e, *key, ids);
|
||||||
|
|
||||||
let created_at = note.created_at();
|
let created_at = note.created_at();
|
||||||
(
|
(
|
||||||
@@ -440,19 +462,43 @@ fn process_event(damus: &mut Damus, _subid: &str, event: &str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_unknown_ids<'a>(txn: &'a Transaction, damus: &Damus) -> Result<Vec<UnknownId<'a>>> {
|
fn get_unknown_ids<'a>(txn: &'a Transaction, damus: &mut Damus) -> Result<Vec<UnknownId<'a>>> {
|
||||||
#[cfg(feature = "profiling")]
|
#[cfg(feature = "profiling")]
|
||||||
puffin::profile_function!();
|
puffin::profile_function!();
|
||||||
|
|
||||||
let mut ids: HashSet<UnknownId> = HashSet::new();
|
let mut ids: HashSet<UnknownId> = HashSet::new();
|
||||||
|
let mut new_cached_notes: Vec<(NoteKey, CachedNote)> = vec![];
|
||||||
|
|
||||||
for timeline in &damus.timelines {
|
for timeline in &damus.timelines {
|
||||||
for noteref in timeline.notes(ViewFilter::NotesAndReplies) {
|
for noteref in timeline.notes(ViewFilter::NotesAndReplies) {
|
||||||
let note = damus.ndb.get_note_by_key(txn, noteref.key)?;
|
let note = damus.ndb.get_note_by_key(txn, noteref.key)?;
|
||||||
let _ = get_unknown_note_ids(&damus.ndb, txn, ¬e, note.key().unwrap(), &mut ids);
|
let note_key = note.key().unwrap();
|
||||||
|
let cached_note = damus.note_cache().cached_note(noteref.key);
|
||||||
|
let cached_note = if let Some(cn) = cached_note {
|
||||||
|
cn.clone()
|
||||||
|
} else {
|
||||||
|
let new_cached_note = CachedNote::new(¬e);
|
||||||
|
new_cached_notes.push((note_key, new_cached_note.clone()));
|
||||||
|
new_cached_note
|
||||||
|
};
|
||||||
|
|
||||||
|
let _ = get_unknown_note_ids(
|
||||||
|
&damus.ndb,
|
||||||
|
&cached_note,
|
||||||
|
txn,
|
||||||
|
¬e,
|
||||||
|
note.key().unwrap(),
|
||||||
|
&mut ids,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is mainly done to avoid the double mutable borrow that would happen
|
||||||
|
// if we tried to update the note_cache mutably in the loop above
|
||||||
|
for (note_key, note) in new_cached_notes {
|
||||||
|
damus.note_cache_mut().cache_mut().insert(note_key, note);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(ids.into_iter().collect())
|
Ok(ids.into_iter().collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -583,6 +629,10 @@ impl Damus {
|
|||||||
&mut self.note_cache
|
&mut self.note_cache
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn note_cache(&self) -> &NoteCache {
|
||||||
|
&self.note_cache
|
||||||
|
}
|
||||||
|
|
||||||
pub fn selected_timeline(&mut self) -> &mut Timeline {
|
pub fn selected_timeline(&mut self) -> &mut Timeline {
|
||||||
&mut self.timelines[self.selected_timeline as usize]
|
&mut self.timelines[self.selected_timeline as usize]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ impl NoteCache {
|
|||||||
.or_insert_with(|| CachedNote::new(note))
|
.or_insert_with(|| CachedNote::new(note))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn cached_note(&self, note_key: NoteKey) -> Option<&CachedNote> {
|
||||||
|
self.cache.get(¬e_key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn cache_mut(&mut self) -> &mut HashMap<NoteKey, CachedNote> {
|
||||||
|
&mut self.cache
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cached_note_or_insert(&mut self, note_key: NoteKey, note: &Note) -> &CachedNote {
|
pub fn cached_note_or_insert(&mut self, note_key: NoteKey, note: &Note) -> &CachedNote {
|
||||||
self.cache
|
self.cache
|
||||||
.entry(note_key)
|
.entry(note_key)
|
||||||
@@ -23,6 +31,7 @@ impl NoteCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct CachedNote {
|
pub struct CachedNote {
|
||||||
reltime: TimeCached<String>,
|
reltime: TimeCached<String>,
|
||||||
pub reply: NoteReplyBuf,
|
pub reply: NoteReplyBuf,
|
||||||
@@ -45,7 +54,11 @@ impl CachedNote {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reltime_str(&mut self) -> &str {
|
pub fn reltime_str_mut(&mut self) -> &str {
|
||||||
return self.reltime.get();
|
self.reltime.get_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn reltime_str(&self) -> Option<&str> {
|
||||||
|
self.reltime.get().map(|x| x.as_str())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,40 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct TimeCached<T> {
|
pub struct TimeCached<T> {
|
||||||
last_update: Instant,
|
last_update: Instant,
|
||||||
expires_in: Duration,
|
expires_in: Duration,
|
||||||
value: Option<T>,
|
value: Option<T>,
|
||||||
refresh: Box<dyn Fn() -> T + 'static>,
|
refresh: Rc<dyn Fn() -> T + 'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TimeCached<T> {
|
impl<T> TimeCached<T> {
|
||||||
pub fn new(expires_in: Duration, refresh: Box<dyn Fn() -> T + 'static>) -> Self {
|
pub fn new(expires_in: Duration, refresh: impl Fn() -> T + 'static) -> Self {
|
||||||
TimeCached {
|
TimeCached {
|
||||||
last_update: Instant::now(),
|
last_update: Instant::now(),
|
||||||
expires_in,
|
expires_in,
|
||||||
value: None,
|
value: None,
|
||||||
refresh,
|
refresh: Rc::new(refresh),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&mut self) -> &T {
|
pub fn needs_update(&self) -> bool {
|
||||||
if self.value.is_none() || self.last_update.elapsed() > self.expires_in {
|
self.value.is_none() || self.last_update.elapsed() > self.expires_in
|
||||||
self.last_update = Instant::now();
|
}
|
||||||
self.value = Some((self.refresh)());
|
|
||||||
|
pub fn update(&mut self) {
|
||||||
|
self.last_update = Instant::now();
|
||||||
|
self.value = Some((self.refresh)());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self) -> Option<&T> {
|
||||||
|
self.value.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self) -> &T {
|
||||||
|
if self.needs_update() {
|
||||||
|
self.update();
|
||||||
}
|
}
|
||||||
self.value.as_ref().unwrap() // This unwrap is safe because we just set the value if it was None.
|
self.value.as_ref().unwrap() // This unwrap is safe because we just set the value if it was None.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -326,7 +326,7 @@ fn render_reltime(
|
|||||||
secondary_label(ui, "⋅");
|
secondary_label(ui, "⋅");
|
||||||
}
|
}
|
||||||
|
|
||||||
secondary_label(ui, note_cache.reltime_str());
|
secondary_label(ui, note_cache.reltime_str_mut());
|
||||||
|
|
||||||
if !before {
|
if !before {
|
||||||
secondary_label(ui, "⋅");
|
secondary_label(ui, "⋅");
|
||||||
|
|||||||
Reference in New Issue
Block a user