notedeck

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

route.rs (6682B)


      1 use enostr::{NoteId, Pubkey};
      2 use nostrdb::Ndb;
      3 use serde::{Deserialize, Serialize};
      4 use std::fmt::{self};
      5 
      6 use crate::{
      7     account_manager::AccountsRoute,
      8     column::Columns,
      9     timeline::{TimelineId, TimelineRoute},
     10     ui::{
     11         add_column::AddColumnRoute,
     12         profile::preview::{get_note_users_displayname_string, get_profile_displayname_string},
     13     },
     14 };
     15 
     16 /// App routing. These describe different places you can go inside Notedeck.
     17 #[derive(Clone, Copy, Eq, PartialEq, Debug, Serialize, Deserialize)]
     18 pub enum Route {
     19     Timeline(TimelineRoute),
     20     Accounts(AccountsRoute),
     21     Relays,
     22     ComposeNote,
     23     AddColumn(AddColumnRoute),
     24     Profile(Pubkey),
     25     Support,
     26 }
     27 
     28 #[derive(Clone)]
     29 pub struct TitledRoute {
     30     pub route: Route,
     31     pub title: String,
     32 }
     33 
     34 impl fmt::Display for TitledRoute {
     35     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     36         write!(f, "{}", self.title)
     37     }
     38 }
     39 
     40 impl Route {
     41     pub fn timeline(timeline_id: TimelineId) -> Self {
     42         Route::Timeline(TimelineRoute::Timeline(timeline_id))
     43     }
     44 
     45     pub fn timeline_id(&self) -> Option<&TimelineId> {
     46         if let Route::Timeline(TimelineRoute::Timeline(tid)) = self {
     47             Some(tid)
     48         } else {
     49             None
     50         }
     51     }
     52 
     53     pub fn relays() -> Self {
     54         Route::Relays
     55     }
     56 
     57     pub fn thread(thread_root: NoteId) -> Self {
     58         Route::Timeline(TimelineRoute::Thread(thread_root))
     59     }
     60 
     61     pub fn reply(replying_to: NoteId) -> Self {
     62         Route::Timeline(TimelineRoute::Reply(replying_to))
     63     }
     64 
     65     pub fn quote(quoting: NoteId) -> Self {
     66         Route::Timeline(TimelineRoute::Quote(quoting))
     67     }
     68 
     69     pub fn accounts() -> Self {
     70         Route::Accounts(AccountsRoute::Accounts)
     71     }
     72 
     73     pub fn add_account() -> Self {
     74         Route::Accounts(AccountsRoute::AddAccount)
     75     }
     76 
     77     pub fn get_titled_route(&self, columns: &Columns, ndb: &Ndb) -> TitledRoute {
     78         let title = match self {
     79             Route::Timeline(tlr) => match tlr {
     80                 TimelineRoute::Timeline(id) => {
     81                     let timeline = columns
     82                         .find_timeline(*id)
     83                         .expect("expected to find timeline");
     84                     timeline.kind.to_title(ndb)
     85                 }
     86                 TimelineRoute::Thread(id) => {
     87                     format!("{}'s Thread", get_note_users_displayname_string(ndb, id))
     88                 }
     89                 TimelineRoute::Reply(id) => {
     90                     format!("{}'s Reply", get_note_users_displayname_string(ndb, id))
     91                 }
     92                 TimelineRoute::Quote(id) => {
     93                     format!("{}'s Quote", get_note_users_displayname_string(ndb, id))
     94                 }
     95             },
     96 
     97             Route::Relays => "Relays".to_owned(),
     98 
     99             Route::Accounts(amr) => match amr {
    100                 AccountsRoute::Accounts => "Accounts".to_owned(),
    101                 AccountsRoute::AddAccount => "Add Account".to_owned(),
    102             },
    103             Route::ComposeNote => "Compose Note".to_owned(),
    104             Route::AddColumn(c) => match c {
    105                 AddColumnRoute::Base => "Add Column".to_owned(),
    106                 AddColumnRoute::UndecidedNotification => "Add Notifications Column".to_owned(),
    107                 AddColumnRoute::ExternalNotification => {
    108                     "Add External Notifications Column".to_owned()
    109                 }
    110             },
    111             Route::Profile(pubkey) => {
    112                 format!("{}'s Profile", get_profile_displayname_string(ndb, pubkey))
    113             }
    114             Route::Support => "Damus Support".to_owned(),
    115         };
    116 
    117         TitledRoute {
    118             title,
    119             route: *self,
    120         }
    121     }
    122 }
    123 
    124 // TODO: add this to egui-nav so we don't have to deal with returning
    125 // and navigating headaches
    126 #[derive(Clone)]
    127 pub struct Router<R: Clone> {
    128     routes: Vec<R>,
    129     pub returning: bool,
    130     pub navigating: bool,
    131     replacing: bool,
    132 }
    133 
    134 impl<R: Clone> Router<R> {
    135     pub fn new(routes: Vec<R>) -> Self {
    136         if routes.is_empty() {
    137             panic!("routes can't be empty")
    138         }
    139         let returning = false;
    140         let navigating = false;
    141         let replacing = false;
    142         Router {
    143             routes,
    144             returning,
    145             navigating,
    146             replacing,
    147         }
    148     }
    149 
    150     pub fn route_to(&mut self, route: R) {
    151         self.navigating = true;
    152         self.routes.push(route);
    153     }
    154 
    155     // Route to R. Then when it is successfully placed, should call `remove_previous_routes` to remove all previous routes
    156     pub fn route_to_replaced(&mut self, route: R) {
    157         self.navigating = true;
    158         self.replacing = true;
    159         self.routes.push(route);
    160     }
    161 
    162     /// Go back, start the returning process
    163     pub fn go_back(&mut self) -> Option<R> {
    164         if self.returning || self.routes.len() == 1 {
    165             return None;
    166         }
    167         self.returning = true;
    168         self.routes.get(self.routes.len() - 2).cloned()
    169     }
    170 
    171     /// Pop a route, should only be called on a NavRespose::Returned reseponse
    172     pub fn pop(&mut self) -> Option<R> {
    173         if self.routes.len() == 1 {
    174             return None;
    175         }
    176         self.returning = false;
    177         self.routes.pop()
    178     }
    179 
    180     pub fn remove_previous_routes(&mut self) {
    181         let num_routes = self.routes.len();
    182         if num_routes <= 1 {
    183             return;
    184         }
    185 
    186         self.returning = false;
    187         self.replacing = false;
    188         self.routes.drain(..num_routes - 1);
    189     }
    190 
    191     pub fn is_replacing(&self) -> bool {
    192         self.replacing
    193     }
    194 
    195     pub fn top(&self) -> &R {
    196         self.routes.last().expect("routes can't be empty")
    197     }
    198 
    199     pub fn routes(&self) -> &Vec<R> {
    200         &self.routes
    201     }
    202 }
    203 
    204 impl fmt::Display for Route {
    205     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    206         match self {
    207             Route::Timeline(tlr) => match tlr {
    208                 TimelineRoute::Timeline(name) => write!(f, "{}", name),
    209                 TimelineRoute::Thread(_id) => write!(f, "Thread"),
    210                 TimelineRoute::Reply(_id) => write!(f, "Reply"),
    211                 TimelineRoute::Quote(_id) => write!(f, "Quote"),
    212             },
    213 
    214             Route::Relays => write!(f, "Relays"),
    215 
    216             Route::Accounts(amr) => match amr {
    217                 AccountsRoute::Accounts => write!(f, "Accounts"),
    218                 AccountsRoute::AddAccount => write!(f, "Add Account"),
    219             },
    220             Route::ComposeNote => write!(f, "Compose Note"),
    221 
    222             Route::AddColumn(_) => write!(f, "Add Column"),
    223             Route::Profile(_) => write!(f, "Profile"),
    224             Route::Support => write!(f, "Support"),
    225         }
    226     }
    227 }