actionbar.rs (4819B)
1 use crate::{ 2 note::NoteRef, 3 route::Route, 4 thread::{Thread, ThreadResult}, 5 Damus, 6 }; 7 use enostr::NoteId; 8 use nostrdb::Transaction; 9 use tracing::{error, info}; 10 use uuid::Uuid; 11 12 #[derive(Debug, Eq, PartialEq, Copy, Clone)] 13 pub enum BarAction { 14 Reply, 15 OpenThread, 16 } 17 18 pub struct NewThreadNotes { 19 pub root_id: NoteId, 20 pub notes: Vec<NoteRef>, 21 } 22 23 pub enum BarResult { 24 NewThreadNotes(NewThreadNotes), 25 } 26 27 /// open_thread is called when a note is selected and we need to navigate 28 /// to a thread It is responsible for managing the subscription and 29 /// making sure the thread is up to date. In a sense, it's a model for 30 /// the thread view. We don't have a concept of model/view/controller etc 31 /// in egui, but this is the closest thing to that. 32 fn open_thread( 33 app: &mut Damus, 34 txn: &Transaction, 35 timeline: usize, 36 selected_note: &[u8; 32], 37 ) -> Option<BarResult> { 38 { 39 let timeline = &mut app.timelines[timeline]; 40 timeline 41 .routes 42 .push(Route::Thread(NoteId::new(selected_note.to_owned()))); 43 timeline.navigating = true; 44 } 45 46 let root_id = crate::note::root_note_id_from_selected_id(app, txn, selected_note); 47 let thread_res = app.threads.thread_mut(&app.ndb, txn, root_id); 48 49 let (thread, result) = match thread_res { 50 ThreadResult::Stale(thread) => { 51 // The thread is stale, let's update it 52 let notes = Thread::new_notes(&thread.view.notes, root_id, txn, &app.ndb); 53 let bar_result = if notes.is_empty() { 54 None 55 } else { 56 Some(BarResult::new_thread_notes( 57 notes, 58 NoteId::new(root_id.to_owned()), 59 )) 60 }; 61 62 // 63 // we can't insert and update the VirtualList now, because we 64 // are already borrowing it mutably. Let's pass it as a 65 // result instead 66 // 67 // thread.view.insert(¬es); <-- no 68 // 69 (thread, bar_result) 70 } 71 72 ThreadResult::Fresh(thread) => (thread, None), 73 }; 74 75 // only start a subscription on nav and if we don't have 76 // an active subscription for this thread. 77 if thread.subscription().is_none() { 78 let filters = Thread::filters(root_id); 79 *thread.subscription_mut() = app.ndb.subscribe(&filters).ok(); 80 81 if thread.remote_subscription().is_some() { 82 error!("Found active remote subscription when it was not expected"); 83 } else { 84 let subid = Uuid::new_v4().to_string(); 85 *thread.remote_subscription_mut() = Some(subid.clone()); 86 app.pool.subscribe(subid, filters); 87 } 88 89 match thread.subscription() { 90 Some(_sub) => { 91 thread.subscribers += 1; 92 info!( 93 "Locally/remotely subscribing to thread. {} total active subscriptions, {} on this thread", 94 app.ndb.subscription_count(), 95 thread.subscribers, 96 ); 97 } 98 None => error!( 99 "Error subscribing locally to selected note '{}''s thread", 100 hex::encode(selected_note) 101 ), 102 } 103 } else { 104 thread.subscribers += 1; 105 info!( 106 "Re-using existing thread subscription. {} total active subscriptions, {} on this thread", 107 app.ndb.subscription_count(), 108 thread.subscribers, 109 ) 110 } 111 112 result 113 } 114 115 impl BarAction { 116 pub fn execute( 117 self, 118 app: &mut Damus, 119 timeline: usize, 120 replying_to: &[u8; 32], 121 txn: &Transaction, 122 ) -> Option<BarResult> { 123 match self { 124 BarAction::Reply => { 125 let timeline = &mut app.timelines[timeline]; 126 timeline 127 .routes 128 .push(Route::Reply(NoteId::new(replying_to.to_owned()))); 129 timeline.navigating = true; 130 None 131 } 132 133 BarAction::OpenThread => open_thread(app, txn, timeline, replying_to), 134 } 135 } 136 } 137 138 impl BarResult { 139 pub fn new_thread_notes(notes: Vec<NoteRef>, root_id: NoteId) -> Self { 140 BarResult::NewThreadNotes(NewThreadNotes::new(notes, root_id)) 141 } 142 } 143 144 impl NewThreadNotes { 145 pub fn new(notes: Vec<NoteRef>, root_id: NoteId) -> Self { 146 NewThreadNotes { notes, root_id } 147 } 148 149 /// Simple helper for processing a NewThreadNotes result. It simply 150 /// inserts/merges the notes into the thread cache 151 pub fn process(&self, thread: &mut Thread) { 152 // threads are chronological, ie reversed from reverse-chronological, the default. 153 let reversed = true; 154 thread.view.insert(&self.notes, reversed); 155 } 156 }