notedeck

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

actionbar.rs (6507B)


      1 use crate::{
      2     column::Columns,
      3     route::{Route, Router},
      4     timeline::{TimelineCache, TimelineCacheKey},
      5 };
      6 
      7 use enostr::{NoteId, Pubkey, RelayPool};
      8 use nostrdb::{Ndb, NoteKey, Transaction};
      9 use notedeck::{note::root_note_id_from_selected_id, NoteCache, RootIdError, UnknownIds};
     10 use tracing::error;
     11 
     12 #[derive(Debug, Eq, PartialEq, Copy, Clone)]
     13 pub enum NoteAction {
     14     Reply(NoteId),
     15     Quote(NoteId),
     16     OpenThread(NoteId),
     17     OpenProfile(Pubkey),
     18 }
     19 
     20 pub struct NewNotes<'a> {
     21     pub id: TimelineCacheKey<'a>,
     22     pub notes: Vec<NoteKey>,
     23 }
     24 
     25 pub enum TimelineOpenResult<'a> {
     26     NewNotes(NewNotes<'a>),
     27 }
     28 
     29 /// open_thread is called when a note is selected and we need to navigate
     30 /// to a thread It is responsible for managing the subscription and
     31 /// making sure the thread is up to date. In a sense, it's a model for
     32 /// the thread view. We don't have a concept of model/view/controller etc
     33 /// in egui, but this is the closest thing to that.
     34 #[allow(clippy::too_many_arguments)]
     35 fn open_thread<'txn>(
     36     ndb: &Ndb,
     37     txn: &'txn Transaction,
     38     router: &mut Router<Route>,
     39     note_cache: &mut NoteCache,
     40     pool: &mut RelayPool,
     41     timeline_cache: &mut TimelineCache,
     42     selected_note: &'txn [u8; 32],
     43 ) -> Option<TimelineOpenResult<'txn>> {
     44     router.route_to(Route::thread(NoteId::new(selected_note.to_owned())));
     45 
     46     match root_note_id_from_selected_id(ndb, note_cache, txn, selected_note) {
     47         Ok(root_id) => timeline_cache.open(
     48             ndb,
     49             note_cache,
     50             txn,
     51             pool,
     52             TimelineCacheKey::thread(root_id),
     53         ),
     54 
     55         Err(RootIdError::NoteNotFound) => {
     56             error!(
     57                 "open_thread: note not found: {}",
     58                 hex::encode(selected_note)
     59             );
     60             None
     61         }
     62 
     63         Err(RootIdError::NoRootId) => {
     64             error!(
     65                 "open_thread: note has no root id: {}",
     66                 hex::encode(selected_note)
     67             );
     68             None
     69         }
     70     }
     71 }
     72 
     73 impl NoteAction {
     74     #[allow(clippy::too_many_arguments)]
     75     pub fn execute<'txn, 'a>(
     76         &'a self,
     77         ndb: &Ndb,
     78         router: &mut Router<Route>,
     79         timeline_cache: &mut TimelineCache,
     80         note_cache: &mut NoteCache,
     81         pool: &mut RelayPool,
     82         txn: &'txn Transaction,
     83     ) -> Option<TimelineOpenResult<'txn>>
     84     where
     85         'a: 'txn,
     86     {
     87         match self {
     88             NoteAction::Reply(note_id) => {
     89                 router.route_to(Route::reply(*note_id));
     90                 None
     91             }
     92 
     93             NoteAction::OpenThread(note_id) => open_thread(
     94                 ndb,
     95                 txn,
     96                 router,
     97                 note_cache,
     98                 pool,
     99                 timeline_cache,
    100                 note_id.bytes(),
    101             ),
    102 
    103             NoteAction::OpenProfile(pubkey) => {
    104                 router.route_to(Route::profile(*pubkey));
    105                 timeline_cache.open(
    106                     ndb,
    107                     note_cache,
    108                     txn,
    109                     pool,
    110                     TimelineCacheKey::profile(pubkey.as_ref()),
    111                 )
    112             }
    113 
    114             NoteAction::Quote(note_id) => {
    115                 router.route_to(Route::quote(*note_id));
    116                 None
    117             }
    118         }
    119     }
    120 
    121     /// Execute the NoteAction and process the TimelineOpenResult
    122     #[allow(clippy::too_many_arguments)]
    123     pub fn execute_and_process_result(
    124         self,
    125         ndb: &Ndb,
    126         columns: &mut Columns,
    127         col: usize,
    128         timeline_cache: &mut TimelineCache,
    129         note_cache: &mut NoteCache,
    130         pool: &mut RelayPool,
    131         txn: &Transaction,
    132         unknown_ids: &mut UnknownIds,
    133     ) {
    134         let router = columns.column_mut(col).router_mut();
    135         if let Some(br) = self.execute(ndb, router, timeline_cache, note_cache, pool, txn) {
    136             br.process(ndb, note_cache, txn, timeline_cache, unknown_ids);
    137         }
    138     }
    139 }
    140 
    141 impl<'a> TimelineOpenResult<'a> {
    142     pub fn new_notes(notes: Vec<NoteKey>, id: TimelineCacheKey<'a>) -> Self {
    143         Self::NewNotes(NewNotes::new(notes, id))
    144     }
    145 
    146     pub fn process(
    147         &self,
    148         ndb: &Ndb,
    149         note_cache: &mut NoteCache,
    150         txn: &Transaction,
    151         storage: &mut TimelineCache,
    152         unknown_ids: &mut UnknownIds,
    153     ) {
    154         match self {
    155             // update the thread for next render if we have new notes
    156             TimelineOpenResult::NewNotes(new_notes) => {
    157                 new_notes.process(storage, ndb, txn, unknown_ids, note_cache);
    158             }
    159         }
    160     }
    161 }
    162 
    163 impl<'a> NewNotes<'a> {
    164     pub fn new(notes: Vec<NoteKey>, id: TimelineCacheKey<'a>) -> Self {
    165         NewNotes { notes, id }
    166     }
    167 
    168     /// Simple helper for processing a NewThreadNotes result. It simply
    169     /// inserts/merges the notes into the corresponding timeline cache
    170     pub fn process(
    171         &self,
    172         timeline_cache: &mut TimelineCache,
    173         ndb: &Ndb,
    174         txn: &Transaction,
    175         unknown_ids: &mut UnknownIds,
    176         note_cache: &mut NoteCache,
    177     ) {
    178         match self.id {
    179             TimelineCacheKey::Profile(pubkey) => {
    180                 let profile = if let Some(profile) = timeline_cache.profiles.get_mut(pubkey.bytes())
    181                 {
    182                     profile
    183                 } else {
    184                     return;
    185                 };
    186 
    187                 let reversed = false;
    188 
    189                 if let Err(err) = profile.timeline.insert(
    190                     &self.notes,
    191                     ndb,
    192                     txn,
    193                     unknown_ids,
    194                     note_cache,
    195                     reversed,
    196                 ) {
    197                     error!("error inserting notes into profile timeline: {err}")
    198                 }
    199             }
    200 
    201             TimelineCacheKey::Thread(root_id) => {
    202                 // threads are chronological, ie reversed from reverse-chronological, the default.
    203                 let reversed = true;
    204                 let thread = if let Some(thread) = timeline_cache.threads.get_mut(root_id.bytes()) {
    205                     thread
    206                 } else {
    207                     return;
    208                 };
    209 
    210                 if let Err(err) =
    211                     thread
    212                         .timeline
    213                         .insert(&self.notes, ndb, txn, unknown_ids, note_cache, reversed)
    214                 {
    215                     error!("error inserting notes into thread timeline: {err}")
    216                 }
    217             }
    218         }
    219     }
    220 }