notedeck

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

route.rs (6201B)


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