notedeck

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

route.rs (6863B)


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