notes_holder.rs (6900B)
1 use std::collections::HashMap; 2 3 use enostr::{Filter, RelayPool}; 4 use nostrdb::{Ndb, Transaction}; 5 use tracing::{debug, info, warn}; 6 7 use crate::{ 8 actionbar::NotesHolderResult, multi_subscriber::MultiSubscriber, muted::MuteFun, note::NoteRef, 9 notecache::NoteCache, timeline::TimelineTab, unknowns::NoteRefsUnkIdAction, Error, Result, 10 }; 11 12 pub struct NotesHolderStorage<M: NotesHolder> { 13 pub id_to_object: HashMap<[u8; 32], M>, 14 } 15 16 impl<M: NotesHolder> Default for NotesHolderStorage<M> { 17 fn default() -> Self { 18 NotesHolderStorage { 19 id_to_object: HashMap::new(), 20 } 21 } 22 } 23 24 pub enum Vitality<'a, M> { 25 Fresh(&'a mut M), 26 Stale(&'a mut M), 27 } 28 29 impl<'a, M> Vitality<'a, M> { 30 pub fn get_ptr(self) -> &'a mut M { 31 match self { 32 Self::Fresh(ptr) => ptr, 33 Self::Stale(ptr) => ptr, 34 } 35 } 36 37 pub fn is_stale(&self) -> bool { 38 match self { 39 Self::Fresh(_ptr) => false, 40 Self::Stale(_ptr) => true, 41 } 42 } 43 } 44 45 impl<M: NotesHolder> NotesHolderStorage<M> { 46 pub fn notes_holder_expected_mut(&mut self, id: &[u8; 32]) -> &mut M { 47 self.id_to_object 48 .get_mut(id) 49 .expect("notes_holder_expected_mut used but there was no NotesHolder") 50 } 51 52 pub fn notes_holder_mutated<'a>( 53 &'a mut self, 54 ndb: &Ndb, 55 note_cache: &mut NoteCache, 56 txn: &Transaction, 57 id: &[u8; 32], 58 is_muted: &MuteFun, 59 ) -> Vitality<'a, M> { 60 // we can't use the naive hashmap entry API here because lookups 61 // require a copy, wait until we have a raw entry api. We could 62 // also use hashbrown? 63 64 if self.id_to_object.contains_key(id) { 65 return Vitality::Stale(self.notes_holder_expected_mut(id)); 66 } 67 68 // we don't have the note holder, query for it! 69 let filters = M::filters(id); 70 71 let notes = if let Ok(results) = ndb.query(txn, &filters, 1000) { 72 results 73 .into_iter() 74 .map(NoteRef::from_query_result) 75 .collect() 76 } else { 77 debug!( 78 "got no results from NotesHolder lookup for {}", 79 hex::encode(id) 80 ); 81 vec![] 82 }; 83 84 if notes.is_empty() { 85 warn!("NotesHolder query returned 0 notes? ") 86 } else { 87 info!("found NotesHolder with {} notes", notes.len()); 88 } 89 90 self.id_to_object.insert( 91 id.to_owned(), 92 M::new_notes_holder(txn, ndb, note_cache, id, M::filters(id), notes, is_muted), 93 ); 94 Vitality::Fresh(self.id_to_object.get_mut(id).unwrap()) 95 } 96 } 97 98 pub trait NotesHolder { 99 fn get_multi_subscriber(&mut self) -> Option<&mut MultiSubscriber>; 100 fn set_multi_subscriber(&mut self, subscriber: MultiSubscriber); 101 fn get_view(&mut self) -> &mut TimelineTab; 102 fn filters(for_id: &[u8; 32]) -> Vec<Filter>; 103 fn filters_since(for_id: &[u8; 32], since: u64) -> Vec<Filter>; 104 fn new_notes_holder( 105 txn: &Transaction, 106 ndb: &Ndb, 107 note_cache: &mut NoteCache, 108 id: &[u8; 32], 109 filters: Vec<Filter>, 110 notes: Vec<NoteRef>, 111 is_muted: &MuteFun, 112 ) -> Self; 113 114 #[must_use = "process_action must be handled in the Ok(action) case"] 115 fn poll_notes_into_view( 116 &mut self, 117 txn: &Transaction, 118 ndb: &Ndb, 119 is_muted: &MuteFun, 120 ) -> Result<NoteRefsUnkIdAction> { 121 if let Some(multi_subscriber) = self.get_multi_subscriber() { 122 let reversed = true; 123 let note_refs: Vec<NoteRef> = multi_subscriber.poll_for_notes(ndb, txn, is_muted)?; 124 self.get_view().insert(¬e_refs, reversed); 125 Ok(NoteRefsUnkIdAction::new(note_refs)) 126 } else { 127 Err(Error::Generic( 128 "NotesHolder unexpectedly has no MultiSubscriber".to_owned(), 129 )) 130 } 131 } 132 133 /// Look for new thread notes since our last fetch 134 fn new_notes(notes: &[NoteRef], id: &[u8; 32], txn: &Transaction, ndb: &Ndb) -> Vec<NoteRef> { 135 if notes.is_empty() { 136 return vec![]; 137 } 138 139 let last_note = notes[0]; 140 let filters = Self::filters_since(id, last_note.created_at + 1); 141 142 if let Ok(results) = ndb.query(txn, &filters, 1000) { 143 debug!("got {} results from NotesHolder update", results.len()); 144 results 145 .into_iter() 146 .map(NoteRef::from_query_result) 147 .collect() 148 } else { 149 debug!("got no results from NotesHolder update",); 150 vec![] 151 } 152 } 153 154 /// Local NotesHolder unsubscribe 155 fn unsubscribe_locally<M: NotesHolder>( 156 txn: &Transaction, 157 ndb: &Ndb, 158 note_cache: &mut NoteCache, 159 notes_holder_storage: &mut NotesHolderStorage<M>, 160 pool: &mut RelayPool, 161 id: &[u8; 32], 162 is_muted: &MuteFun, 163 ) { 164 let notes_holder = notes_holder_storage 165 .notes_holder_mutated(ndb, note_cache, txn, id, is_muted) 166 .get_ptr(); 167 168 if let Some(multi_subscriber) = notes_holder.get_multi_subscriber() { 169 multi_subscriber.unsubscribe(ndb, pool); 170 } 171 } 172 173 fn open<M: NotesHolder>( 174 ndb: &Ndb, 175 note_cache: &mut NoteCache, 176 txn: &Transaction, 177 pool: &mut RelayPool, 178 storage: &mut NotesHolderStorage<M>, 179 id: &[u8; 32], 180 is_muted: &MuteFun, 181 ) -> Option<NotesHolderResult> { 182 let vitality = storage.notes_holder_mutated(ndb, note_cache, txn, id, is_muted); 183 184 let (holder, result) = match vitality { 185 Vitality::Stale(holder) => { 186 // The NotesHolder is stale, let's update it 187 let notes = M::new_notes(&holder.get_view().notes, id, txn, ndb); 188 let holder_result = if notes.is_empty() { 189 None 190 } else { 191 Some(NotesHolderResult::new_notes(notes, id.to_owned())) 192 }; 193 194 // 195 // we can't insert and update the VirtualList now, because we 196 // are already borrowing it mutably. Let's pass it as a 197 // result instead 198 // 199 // holder.get_view().insert(¬es); <-- no 200 // 201 (holder, holder_result) 202 } 203 204 Vitality::Fresh(thread) => (thread, None), 205 }; 206 207 let multi_subscriber = if let Some(multi_subscriber) = holder.get_multi_subscriber() { 208 multi_subscriber 209 } else { 210 let filters = M::filters(id); 211 holder.set_multi_subscriber(MultiSubscriber::new(filters)); 212 holder.get_multi_subscriber().unwrap() 213 }; 214 215 multi_subscriber.subscribe(ndb, pool); 216 217 result 218 } 219 }