notedeck

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

nav.rs (9104B)


      1 use crate::{
      2     accounts::render_accounts_route,
      3     actionbar::NoteAction,
      4     notes_holder::NotesHolder,
      5     profile::Profile,
      6     relay_pool_manager::RelayPoolManager,
      7     route::Route,
      8     thread::Thread,
      9     timeline::{
     10         route::{render_timeline_route, TimelineRoute},
     11         Timeline,
     12     },
     13     ui::{
     14         self,
     15         add_column::render_add_column_routes,
     16         column::NavTitle,
     17         note::{PostAction, PostType},
     18         support::SupportView,
     19         RelayView, View,
     20     },
     21     Damus,
     22 };
     23 
     24 use egui_nav::{Nav, NavAction, NavResponse, NavUiType};
     25 use nostrdb::{Ndb, Transaction};
     26 use tracing::{error, info};
     27 
     28 pub enum RenderNavAction {
     29     Back,
     30     RemoveColumn,
     31     PostAction(PostAction),
     32     NoteAction(NoteAction),
     33 }
     34 
     35 impl From<PostAction> for RenderNavAction {
     36     fn from(post_action: PostAction) -> Self {
     37         Self::PostAction(post_action)
     38     }
     39 }
     40 
     41 impl From<NoteAction> for RenderNavAction {
     42     fn from(note_action: NoteAction) -> RenderNavAction {
     43         Self::NoteAction(note_action)
     44     }
     45 }
     46 
     47 pub type NotedeckNavResponse = NavResponse<Option<RenderNavAction>>;
     48 
     49 pub struct RenderNavResponse {
     50     column: usize,
     51     response: NotedeckNavResponse,
     52 }
     53 
     54 impl RenderNavResponse {
     55     #[allow(private_interfaces)]
     56     pub fn new(column: usize, response: NotedeckNavResponse) -> Self {
     57         RenderNavResponse { column, response }
     58     }
     59 
     60     #[must_use = "Make sure to save columns if result is true"]
     61     pub fn process_render_nav_response(&self, app: &mut Damus) -> bool {
     62         let mut col_changed: bool = false;
     63         let col = self.column;
     64 
     65         if let Some(action) = self
     66             .response
     67             .response
     68             .as_ref()
     69             .or(self.response.title_response.as_ref())
     70         {
     71             // start returning when we're finished posting
     72             match action {
     73                 RenderNavAction::Back => {
     74                     app.columns_mut().column_mut(col).router_mut().go_back();
     75                 }
     76 
     77                 RenderNavAction::RemoveColumn => {
     78                     let tl = app.columns().find_timeline_for_column_index(col);
     79                     if let Some(timeline) = tl {
     80                         unsubscribe_timeline(app.ndb(), timeline);
     81                     }
     82 
     83                     app.columns_mut().delete_column(col);
     84                     col_changed = true;
     85                 }
     86 
     87                 RenderNavAction::PostAction(post_action) => {
     88                     let txn = Transaction::new(&app.ndb).expect("txn");
     89                     let _ = post_action.execute(&app.ndb, &txn, &mut app.pool, &mut app.drafts);
     90                     app.columns_mut().column_mut(col).router_mut().go_back();
     91                 }
     92 
     93                 RenderNavAction::NoteAction(note_action) => {
     94                     let txn = Transaction::new(&app.ndb).expect("txn");
     95 
     96                     note_action.execute_and_process_result(
     97                         &app.ndb,
     98                         &mut app.columns,
     99                         col,
    100                         &mut app.threads,
    101                         &mut app.profiles,
    102                         &mut app.note_cache,
    103                         &mut app.pool,
    104                         &txn,
    105                         &app.accounts.mutefun(),
    106                     );
    107                 }
    108             }
    109         }
    110 
    111         if let Some(action) = self.response.action {
    112             match action {
    113                 NavAction::Returned => {
    114                     let r = app.columns_mut().column_mut(col).router_mut().pop();
    115                     let txn = Transaction::new(&app.ndb).expect("txn");
    116                     if let Some(Route::Timeline(TimelineRoute::Thread(id))) = r {
    117                         let root_id = {
    118                             crate::note::root_note_id_from_selected_id(
    119                                 &app.ndb,
    120                                 &mut app.note_cache,
    121                                 &txn,
    122                                 id.bytes(),
    123                             )
    124                         };
    125                         Thread::unsubscribe_locally(
    126                             &txn,
    127                             &app.ndb,
    128                             &mut app.note_cache,
    129                             &mut app.threads,
    130                             &mut app.pool,
    131                             root_id,
    132                             &app.accounts.mutefun(),
    133                         );
    134                     }
    135 
    136                     if let Some(Route::Timeline(TimelineRoute::Profile(pubkey))) = r {
    137                         Profile::unsubscribe_locally(
    138                             &txn,
    139                             &app.ndb,
    140                             &mut app.note_cache,
    141                             &mut app.profiles,
    142                             &mut app.pool,
    143                             pubkey.bytes(),
    144                             &app.accounts.mutefun(),
    145                         );
    146                     }
    147                     col_changed = true;
    148                 }
    149 
    150                 NavAction::Navigated => {
    151                     let cur_router = app.columns_mut().column_mut(col).router_mut();
    152                     cur_router.navigating = false;
    153                     if cur_router.is_replacing() {
    154                         cur_router.remove_previous_routes();
    155                     }
    156                     col_changed = true;
    157                 }
    158 
    159                 NavAction::Dragging => {}
    160                 NavAction::Returning => {}
    161                 NavAction::Resetting => {}
    162                 NavAction::Navigating => {}
    163             }
    164         }
    165 
    166         col_changed
    167     }
    168 }
    169 
    170 fn render_nav_body(
    171     ui: &mut egui::Ui,
    172     app: &mut Damus,
    173     top: &Route,
    174     col: usize,
    175 ) -> Option<RenderNavAction> {
    176     match top {
    177         Route::Timeline(tlr) => render_timeline_route(
    178             &app.ndb,
    179             &mut app.columns,
    180             &mut app.drafts,
    181             &mut app.img_cache,
    182             &mut app.unknown_ids,
    183             &mut app.note_cache,
    184             &mut app.threads,
    185             &mut app.profiles,
    186             &mut app.accounts,
    187             *tlr,
    188             col,
    189             app.textmode,
    190             ui,
    191         ),
    192         Route::Accounts(amr) => {
    193             let action = render_accounts_route(
    194                 ui,
    195                 &app.ndb,
    196                 col,
    197                 &mut app.columns,
    198                 &mut app.img_cache,
    199                 &mut app.accounts,
    200                 &mut app.view_state.login,
    201                 *amr,
    202             );
    203             let txn = Transaction::new(&app.ndb).expect("txn");
    204             action.process_action(&mut app.unknown_ids, &app.ndb, &txn);
    205             None
    206         }
    207         Route::Relays => {
    208             let manager = RelayPoolManager::new(app.pool_mut());
    209             RelayView::new(manager).ui(ui);
    210             None
    211         }
    212         Route::ComposeNote => {
    213             let kp = app.accounts.get_selected_account()?.to_full()?;
    214             let draft = app.drafts.compose_mut();
    215 
    216             let txn = Transaction::new(&app.ndb).expect("txn");
    217             let post_response = ui::PostView::new(
    218                 &app.ndb,
    219                 draft,
    220                 PostType::New,
    221                 &mut app.img_cache,
    222                 &mut app.note_cache,
    223                 kp,
    224             )
    225             .ui(&txn, ui);
    226 
    227             post_response.action.map(Into::into)
    228         }
    229         Route::AddColumn(route) => {
    230             render_add_column_routes(ui, app, col, route);
    231 
    232             None
    233         }
    234 
    235         Route::Support => {
    236             SupportView::new(&mut app.support).show(ui);
    237             None
    238         }
    239     }
    240 }
    241 
    242 #[must_use = "RenderNavResponse must be handled by calling .process_render_nav_response(..)"]
    243 pub fn render_nav(col: usize, app: &mut Damus, ui: &mut egui::Ui) -> RenderNavResponse {
    244     let col_id = app.columns.get_column_id_at_index(col);
    245     // TODO(jb55): clean up this router_mut mess by using Router<R> in egui-nav directly
    246 
    247     let nav_response = Nav::new(&app.columns().column(col).router().routes().clone())
    248         .navigating(app.columns_mut().column_mut(col).router_mut().navigating)
    249         .returning(app.columns_mut().column_mut(col).router_mut().returning)
    250         .id_source(egui::Id::new(col_id))
    251         .show_mut(ui, |ui, render_type, nav| match render_type {
    252             NavUiType::Title => NavTitle::new(
    253                 &app.ndb,
    254                 &mut app.img_cache,
    255                 &app.columns,
    256                 app.accounts.get_selected_account().map(|a| &a.pubkey),
    257                 nav.routes(),
    258             )
    259             .show(ui),
    260             NavUiType::Body => render_nav_body(ui, app, nav.routes().last().expect("top"), col),
    261         });
    262 
    263     RenderNavResponse::new(col, nav_response)
    264 }
    265 
    266 fn unsubscribe_timeline(ndb: &Ndb, timeline: &Timeline) {
    267     if let Some(sub_id) = timeline.subscription {
    268         if let Err(e) = ndb.unsubscribe(sub_id) {
    269             error!("unsubscribe error: {}", e);
    270         } else {
    271             info!(
    272                 "successfully unsubscribed from timeline {} with sub id {}",
    273                 timeline.id,
    274                 sub_id.id()
    275             );
    276         }
    277     }
    278 }