column.rs (8050B)
1 use crate::route::{Route, Router}; 2 use crate::timeline::{SerializableTimeline, Timeline, TimelineId, TimelineRoute}; 3 use indexmap::IndexMap; 4 use nostrdb::Ndb; 5 use serde::{Deserialize, Deserializer, Serialize}; 6 use std::iter::Iterator; 7 use std::sync::atomic::{AtomicU32, Ordering}; 8 use tracing::{error, warn}; 9 10 #[derive(Clone)] 11 pub struct Column { 12 router: Router<Route>, 13 } 14 15 impl Column { 16 pub fn new(routes: Vec<Route>) -> Self { 17 let router = Router::new(routes); 18 Column { router } 19 } 20 21 pub fn router(&self) -> &Router<Route> { 22 &self.router 23 } 24 25 pub fn router_mut(&mut self) -> &mut Router<Route> { 26 &mut self.router 27 } 28 } 29 30 impl serde::Serialize for Column { 31 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> 32 where 33 S: serde::Serializer, 34 { 35 self.router.routes().serialize(serializer) 36 } 37 } 38 39 impl<'de> Deserialize<'de> for Column { 40 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 41 where 42 D: Deserializer<'de>, 43 { 44 let routes = Vec::<Route>::deserialize(deserializer)?; 45 46 Ok(Column { 47 router: Router::new(routes), 48 }) 49 } 50 } 51 52 #[derive(Default)] 53 pub struct Columns { 54 /// Columns are simply routers into settings, timelines, etc 55 columns: IndexMap<u32, Column>, 56 57 /// Timeline state is not tied to routing logic separately, so that 58 /// different columns can navigate to and from settings to timelines, 59 /// etc. 60 pub timelines: IndexMap<u32, Timeline>, 61 62 /// The selected column for key navigation 63 selected: i32, 64 } 65 static UIDS: AtomicU32 = AtomicU32::new(0); 66 67 impl Columns { 68 pub fn new() -> Self { 69 Columns::default() 70 } 71 72 pub fn add_new_timeline_column(&mut self, timeline: Timeline) { 73 let id = Self::get_new_id(); 74 let routes = vec![Route::timeline(timeline.id)]; 75 self.timelines.insert(id, timeline); 76 self.columns.insert(id, Column::new(routes)); 77 } 78 79 pub fn add_timeline_to_column(&mut self, col: usize, timeline: Timeline) { 80 let col_id = self.get_column_id_at_index(col); 81 self.column_mut(col) 82 .router_mut() 83 .route_to_replaced(Route::timeline(timeline.id)); 84 self.timelines.insert(col_id, timeline); 85 } 86 87 pub fn new_column_picker(&mut self) { 88 self.add_column(Column::new(vec![Route::AddColumn( 89 crate::ui::add_column::AddColumnRoute::Base, 90 )])); 91 } 92 93 fn get_new_id() -> u32 { 94 UIDS.fetch_add(1, Ordering::Relaxed) 95 } 96 97 pub fn add_column_at(&mut self, column: Column, index: u32) { 98 self.columns.insert(index, column); 99 } 100 101 pub fn add_column(&mut self, column: Column) { 102 self.columns.insert(Self::get_new_id(), column); 103 } 104 105 pub fn columns_mut(&mut self) -> Vec<&mut Column> { 106 self.columns.values_mut().collect() 107 } 108 109 pub fn num_columns(&self) -> usize { 110 self.columns.len() 111 } 112 113 // Get the first router in the columns if there are columns present. 114 // Otherwise, create a new column picker and return the router 115 pub fn get_first_router(&mut self) -> &mut Router<Route> { 116 if self.columns.is_empty() { 117 self.new_column_picker(); 118 } 119 self.columns 120 .get_index_mut(0) 121 .expect("There should be at least one column") 122 .1 123 .router_mut() 124 } 125 126 pub fn timeline_mut(&mut self, timeline_ind: usize) -> &mut Timeline { 127 self.timelines 128 .get_index_mut(timeline_ind) 129 .expect("expected index to be in bounds") 130 .1 131 } 132 133 pub fn column(&self, ind: usize) -> &Column { 134 self.columns 135 .get_index(ind) 136 .expect("Expected index to be in bounds") 137 .1 138 } 139 140 pub fn columns(&self) -> Vec<&Column> { 141 self.columns.values().collect() 142 } 143 144 pub fn get_column_id_at_index(&self, ind: usize) -> u32 { 145 *self 146 .columns 147 .get_index(ind) 148 .expect("expected index to be within bounds") 149 .0 150 } 151 152 pub fn selected(&mut self) -> &mut Column { 153 self.columns 154 .get_index_mut(self.selected as usize) 155 .expect("Expected selected index to be in bounds") 156 .1 157 } 158 159 pub fn timelines_mut(&mut self) -> Vec<&mut Timeline> { 160 self.timelines.values_mut().collect() 161 } 162 163 pub fn timelines(&self) -> Vec<&Timeline> { 164 self.timelines.values().collect() 165 } 166 167 pub fn find_timeline_mut(&mut self, id: TimelineId) -> Option<&mut Timeline> { 168 self.timelines_mut().into_iter().find(|tl| tl.id == id) 169 } 170 171 pub fn find_timeline(&self, id: TimelineId) -> Option<&Timeline> { 172 self.timelines().into_iter().find(|tl| tl.id == id) 173 } 174 175 pub fn column_mut(&mut self, ind: usize) -> &mut Column { 176 self.columns 177 .get_index_mut(ind) 178 .expect("Expected index to be in bounds") 179 .1 180 } 181 182 pub fn find_timeline_for_column_index(&self, ind: usize) -> Option<&Timeline> { 183 let col_id = self.get_column_id_at_index(ind); 184 self.timelines.get(&col_id) 185 } 186 187 pub fn select_down(&mut self) { 188 warn!("todo: implement select_down"); 189 } 190 191 pub fn select_up(&mut self) { 192 warn!("todo: implement select_up"); 193 } 194 195 pub fn select_left(&mut self) { 196 if self.selected - 1 < 0 { 197 return; 198 } 199 self.selected -= 1; 200 } 201 202 pub fn select_right(&mut self) { 203 if self.selected + 1 >= self.columns.len() as i32 { 204 return; 205 } 206 self.selected += 1; 207 } 208 209 pub fn delete_column(&mut self, index: usize) { 210 if let Some((key, _)) = self.columns.get_index_mut(index) { 211 self.timelines.shift_remove(key); 212 } 213 214 self.columns.shift_remove_index(index); 215 216 if self.columns.is_empty() { 217 self.new_column_picker(); 218 } 219 } 220 221 pub fn as_serializable_columns(&self) -> SerializableColumns { 222 SerializableColumns { 223 columns: self.columns.values().cloned().collect(), 224 timelines: self 225 .timelines 226 .values() 227 .map(|t| t.as_serializable_timeline()) 228 .collect(), 229 } 230 } 231 } 232 233 #[derive(Serialize, Deserialize)] 234 pub struct SerializableColumns { 235 pub columns: Vec<Column>, 236 pub timelines: Vec<SerializableTimeline>, 237 } 238 239 impl SerializableColumns { 240 pub fn into_columns(self, ndb: &Ndb, deck_pubkey: Option<&[u8; 32]>) -> Columns { 241 let mut columns = Columns::default(); 242 243 for column in self.columns { 244 let id = Columns::get_new_id(); 245 let mut routes = Vec::new(); 246 for route in column.router.routes() { 247 match route { 248 Route::Timeline(TimelineRoute::Timeline(timeline_id)) => { 249 if let Some(serializable_tl) = 250 self.timelines.iter().find(|tl| tl.id == *timeline_id) 251 { 252 let tl = serializable_tl.clone().into_timeline(ndb, deck_pubkey); 253 if let Some(tl) = tl { 254 routes.push(Route::Timeline(TimelineRoute::Timeline(tl.id))); 255 columns.timelines.insert(id, tl); 256 } else { 257 error!("Problem deserializing timeline {:?}", serializable_tl); 258 } 259 } 260 } 261 Route::Timeline(TimelineRoute::Thread(_thread)) => { 262 // TODO: open thread before pushing route 263 } 264 Route::Profile(_profile) => { 265 // TODO: open profile before pushing route 266 } 267 _ => routes.push(*route), 268 } 269 } 270 columns.add_column_at(Column::new(routes), id); 271 } 272 273 columns 274 } 275 }