column.rs (7250B)
1 use crate::{ 2 actionbar::TimelineOpenResult, 3 drag::DragSwitch, 4 route::{Route, Router, SingletonRouter}, 5 timeline::{Timeline, TimelineCache, TimelineKind}, 6 }; 7 use enostr::RelayPool; 8 use nostrdb::{Ndb, Transaction}; 9 use notedeck::NoteCache; 10 use std::iter::Iterator; 11 use tracing::warn; 12 13 #[derive(Clone, Debug)] 14 pub struct Column { 15 pub router: Router<Route>, 16 pub sheet_router: SingletonRouter<Route>, 17 pub drag: DragSwitch, 18 } 19 20 impl Column { 21 pub fn new(routes: Vec<Route>) -> Self { 22 let router = Router::new(routes); 23 Column { 24 router, 25 sheet_router: SingletonRouter::default(), 26 drag: DragSwitch::default(), 27 } 28 } 29 30 pub fn router(&self) -> &Router<Route> { 31 &self.router 32 } 33 34 pub fn router_mut(&mut self) -> &mut Router<Route> { 35 &mut self.router 36 } 37 } 38 39 #[derive(Default, Debug)] 40 pub struct Columns { 41 /// Columns are simply routers into settings, timelines, etc 42 columns: Vec<Column>, 43 44 /// The selected column for key navigation 45 pub selected: i32, 46 } 47 48 /// When selecting columns, return what happened 49 pub enum SelectionResult { 50 /// We're already selecting that 51 AlreadySelected(usize), 52 53 /// New selection success! 54 NewSelection(usize), 55 56 /// Failed to make a selection 57 Failed, 58 } 59 60 impl Columns { 61 pub fn new() -> Self { 62 Columns::default() 63 } 64 65 /// Choose which column is selected. If in narrow mode, this 66 /// decides which column to render in the main view 67 pub fn select_column(&mut self, index: i32) { 68 let len = self.columns.len(); 69 70 if index < (len as i32) { 71 self.selected = index; 72 } 73 } 74 75 /// Select the column based on the timeline kind. 76 /// 77 /// TODO: add timeline if missing? 78 pub fn select_by_kind(&mut self, kind: &TimelineKind) -> SelectionResult { 79 for (i, col) in self.columns.iter().enumerate() { 80 for route in col.router().routes() { 81 if let Some(timeline) = route.timeline_id() { 82 if timeline == kind { 83 tracing::info!("selecting {kind:?} column"); 84 if self.selected as usize == i { 85 return SelectionResult::AlreadySelected(i); 86 } else { 87 self.select_column(i as i32); 88 return SelectionResult::NewSelection(i); 89 } 90 } 91 } 92 } 93 } 94 95 tracing::error!("failed to select {kind:?} column"); 96 SelectionResult::Failed 97 } 98 99 pub fn add_new_timeline_column( 100 &mut self, 101 timeline_cache: &mut TimelineCache, 102 txn: &Transaction, 103 ndb: &Ndb, 104 note_cache: &mut NoteCache, 105 pool: &mut RelayPool, 106 kind: &TimelineKind, 107 ) -> Option<TimelineOpenResult> { 108 self.columns 109 .push(Column::new(vec![Route::timeline(kind.to_owned())])); 110 timeline_cache.open(ndb, note_cache, txn, pool, kind) 111 } 112 113 pub fn new_column_picker(&mut self) { 114 self.add_column(Column::new(vec![Route::AddColumn( 115 crate::ui::add_column::AddColumnRoute::Base, 116 )])); 117 } 118 119 pub fn insert_intermediary_routes( 120 &mut self, 121 timeline_cache: &mut TimelineCache, 122 intermediary_routes: Vec<IntermediaryRoute>, 123 ) { 124 let routes = intermediary_routes 125 .into_iter() 126 .map(|r| match r { 127 IntermediaryRoute::Timeline(mut timeline) => { 128 let route = Route::timeline(timeline.kind.clone()); 129 timeline.subscription.increment(); 130 timeline_cache.insert(timeline.kind.clone(), *timeline); 131 route 132 } 133 IntermediaryRoute::Route(route) => route, 134 }) 135 .collect(); 136 137 self.columns.push(Column::new(routes)); 138 } 139 140 #[inline] 141 pub fn add_column_at(&mut self, column: Column, index: u32) { 142 self.columns.insert(index as usize, column); 143 } 144 145 #[inline] 146 pub fn add_column(&mut self, column: Column) { 147 self.columns.push(column); 148 } 149 150 #[inline] 151 pub fn columns_mut(&mut self) -> &mut Vec<Column> { 152 &mut self.columns 153 } 154 155 #[inline] 156 pub fn num_columns(&self) -> usize { 157 self.columns.len() 158 } 159 160 // Get the first router in the columns if there are columns present. 161 // Otherwise, create a new column picker and return the router 162 pub fn get_selected_router(&mut self) -> &mut Router<Route> { 163 self.ensure_column(); 164 self.selected_mut().router_mut() 165 } 166 167 #[inline] 168 pub fn column(&self, ind: usize) -> &Column { 169 &self.columns[ind] 170 } 171 172 #[inline] 173 pub fn columns(&self) -> &[Column] { 174 &self.columns 175 } 176 177 #[inline] 178 pub fn selected(&self) -> Option<&Column> { 179 if self.columns.is_empty() { 180 return None; 181 } 182 Some(&self.columns[self.selected as usize]) 183 } 184 185 // TODO(jb55): switch to non-empty container for columns? 186 fn ensure_column(&mut self) { 187 if self.columns.is_empty() { 188 self.new_column_picker(); 189 } 190 } 191 192 /// Get the selected column. If you're looking to route something 193 /// and you're not sure which one to choose, use this one 194 #[inline] 195 pub fn selected_mut(&mut self) -> &mut Column { 196 self.ensure_column(); 197 assert!(self.selected < self.columns.len() as i32); 198 &mut self.columns[self.selected as usize] 199 } 200 201 #[inline] 202 pub fn column_mut(&mut self, ind: usize) -> &mut Column { 203 self.ensure_column(); 204 &mut self.columns[ind] 205 } 206 207 pub fn select_down(&mut self) { 208 warn!("todo: implement select_down"); 209 } 210 211 pub fn select_up(&mut self) { 212 warn!("todo: implement select_up"); 213 } 214 215 pub fn select_left(&mut self) { 216 if self.selected - 1 < 0 { 217 return; 218 } 219 self.selected -= 1; 220 } 221 222 pub fn select_right(&mut self) { 223 if self.selected + 1 >= self.columns.len() as i32 { 224 return; 225 } 226 self.selected += 1; 227 } 228 229 #[must_use = "you must call timeline_cache.pop() for each returned value"] 230 pub fn delete_column(&mut self, index: usize) -> Vec<TimelineKind> { 231 let mut kinds_to_pop: Vec<TimelineKind> = vec![]; 232 for route in self.columns[index].router().routes() { 233 if let Route::Timeline(kind) = route { 234 kinds_to_pop.push(kind.clone()); 235 } 236 } 237 238 self.columns.remove(index); 239 240 if self.columns.is_empty() { 241 self.new_column_picker(); 242 } 243 244 kinds_to_pop 245 } 246 247 pub fn move_col(&mut self, from_index: usize, to_index: usize) { 248 if from_index == to_index 249 || from_index >= self.columns.len() 250 || to_index >= self.columns.len() 251 { 252 return; 253 } 254 255 self.columns.swap(from_index, to_index); 256 } 257 } 258 259 pub enum IntermediaryRoute { 260 Timeline(Box<Timeline>), 261 Route(Route), 262 } 263 264 pub enum ColumnsAction { 265 Switch(usize, usize), // from Switch.0 to Switch.1, 266 Remove(usize), 267 }