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