notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

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(&notes); <-- 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 }