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