notedeck

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

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 }