notedeck

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

decks.rs (9131B)


      1 use std::collections::{hash_map::ValuesMut, HashMap};
      2 
      3 use enostr::Pubkey;
      4 use nostrdb::Ndb;
      5 use tracing::{error, info};
      6 
      7 use crate::{
      8     accounts::AccountsRoute,
      9     column::{Column, Columns},
     10     route::Route,
     11     timeline::{self, Timeline, TimelineKind},
     12     ui::{add_column::AddColumnRoute, configure_deck::ConfigureDeckResponse},
     13 };
     14 
     15 pub static FALLBACK_PUBKEY: fn() -> Pubkey = || {
     16     Pubkey::from_hex("aa733081e4f0f79dd43023d8983265593f2b41a988671cfcef3f489b91ad93fe").unwrap()
     17 };
     18 
     19 pub enum DecksAction {
     20     Switch(usize),
     21     Removing(usize),
     22 }
     23 
     24 pub struct DecksCache {
     25     account_to_decks: HashMap<Pubkey, Decks>,
     26     fallback_pubkey: Pubkey,
     27 }
     28 
     29 impl Default for DecksCache {
     30     fn default() -> Self {
     31         let mut account_to_decks: HashMap<Pubkey, Decks> = Default::default();
     32         account_to_decks.insert(FALLBACK_PUBKEY(), Decks::default());
     33         DecksCache::new(account_to_decks)
     34     }
     35 }
     36 
     37 impl DecksCache {
     38     pub fn new(account_to_decks: HashMap<Pubkey, Decks>) -> Self {
     39         let fallback_pubkey = FALLBACK_PUBKEY();
     40 
     41         Self {
     42             account_to_decks,
     43             fallback_pubkey,
     44         }
     45     }
     46 
     47     pub fn new_with_demo_config(ndb: &Ndb) -> Self {
     48         let mut account_to_decks: HashMap<Pubkey, Decks> = Default::default();
     49         let fallback_pubkey = FALLBACK_PUBKEY();
     50         account_to_decks.insert(fallback_pubkey, demo_decks(fallback_pubkey, ndb));
     51         DecksCache::new(account_to_decks)
     52     }
     53 
     54     pub fn decks(&self, key: &Pubkey) -> &Decks {
     55         self.account_to_decks
     56             .get(key)
     57             .unwrap_or_else(|| self.fallback())
     58     }
     59 
     60     pub fn decks_mut(&mut self, key: &Pubkey) -> &mut Decks {
     61         self.account_to_decks.entry(*key).or_default()
     62     }
     63 
     64     pub fn fallback(&self) -> &Decks {
     65         self.account_to_decks
     66             .get(&self.fallback_pubkey)
     67             .unwrap_or_else(|| panic!("fallback deck not found"))
     68     }
     69 
     70     pub fn fallback_mut(&mut self) -> &mut Decks {
     71         self.account_to_decks
     72             .get_mut(&self.fallback_pubkey)
     73             .unwrap_or_else(|| panic!("fallback deck not found"))
     74     }
     75 
     76     pub fn add_deck_default(&mut self, key: Pubkey) {
     77         self.account_to_decks.insert(key, Decks::default());
     78         info!(
     79             "Adding new default deck for {:?}. New decks size is {}",
     80             key,
     81             self.account_to_decks.get(&key).unwrap().decks.len()
     82         );
     83     }
     84 
     85     pub fn add_decks(&mut self, key: Pubkey, decks: Decks) {
     86         self.account_to_decks.insert(key, decks);
     87         info!(
     88             "Adding new deck for {:?}. New decks size is {}",
     89             key,
     90             self.account_to_decks.get(&key).unwrap().decks.len()
     91         );
     92     }
     93 
     94     pub fn add_deck(&mut self, key: Pubkey, deck: Deck) {
     95         match self.account_to_decks.entry(key) {
     96             std::collections::hash_map::Entry::Occupied(mut entry) => {
     97                 let decks = entry.get_mut();
     98                 decks.add_deck(deck);
     99                 info!(
    100                     "Created new deck for {:?}. New number of decks is {}",
    101                     key,
    102                     decks.decks.len()
    103                 );
    104             }
    105             std::collections::hash_map::Entry::Vacant(entry) => {
    106                 info!("Created first deck for {:?}", key);
    107                 entry.insert(Decks::new(deck));
    108             }
    109         }
    110     }
    111 
    112     pub fn remove_for(&mut self, key: &Pubkey) {
    113         info!("Removing decks for {:?}", key);
    114         self.account_to_decks.remove(key);
    115     }
    116 
    117     pub fn get_fallback_pubkey(&self) -> &Pubkey {
    118         &self.fallback_pubkey
    119     }
    120 
    121     pub fn get_all_decks_mut(&mut self) -> ValuesMut<Pubkey, Decks> {
    122         self.account_to_decks.values_mut()
    123     }
    124 
    125     pub fn get_mapping(&self) -> &HashMap<Pubkey, Decks> {
    126         &self.account_to_decks
    127     }
    128 }
    129 
    130 pub struct Decks {
    131     active_deck: usize,
    132     removal_request: Option<usize>,
    133     decks: Vec<Deck>,
    134 }
    135 
    136 impl Default for Decks {
    137     fn default() -> Self {
    138         Decks::new(Deck::default())
    139     }
    140 }
    141 
    142 impl Decks {
    143     pub fn new(deck: Deck) -> Self {
    144         let decks = vec![deck];
    145 
    146         Decks {
    147             active_deck: 0,
    148             removal_request: None,
    149             decks,
    150         }
    151     }
    152 
    153     pub fn from_decks(active_deck: usize, decks: Vec<Deck>) -> Self {
    154         Self {
    155             active_deck,
    156             removal_request: None,
    157             decks,
    158         }
    159     }
    160 
    161     pub fn active(&self) -> &Deck {
    162         self.decks
    163             .get(self.active_deck)
    164             .expect("active_deck index was invalid")
    165     }
    166 
    167     pub fn active_mut(&mut self) -> &mut Deck {
    168         self.decks
    169             .get_mut(self.active_deck)
    170             .expect("active_deck index was invalid")
    171     }
    172 
    173     pub fn decks(&self) -> &Vec<Deck> {
    174         &self.decks
    175     }
    176 
    177     pub fn decks_mut(&mut self) -> &mut Vec<Deck> {
    178         &mut self.decks
    179     }
    180 
    181     pub fn add_deck(&mut self, deck: Deck) {
    182         self.decks.push(deck);
    183     }
    184 
    185     pub fn active_index(&self) -> usize {
    186         self.active_deck
    187     }
    188 
    189     pub fn set_active(&mut self, index: usize) {
    190         if index < self.decks.len() {
    191             self.active_deck = index;
    192         } else {
    193             error!(
    194                 "requested deck change that is invalid. decks len: {}, requested index: {}",
    195                 self.decks.len(),
    196                 index
    197             );
    198         }
    199     }
    200 
    201     pub fn remove_deck(&mut self, index: usize) {
    202         if index < self.decks.len() {
    203             if self.decks.len() > 1 {
    204                 self.decks.remove(index);
    205 
    206                 let info_prefix = format!("Removed deck at index {}", index);
    207                 match index.cmp(&self.active_deck) {
    208                     std::cmp::Ordering::Less => {
    209                         info!(
    210                             "{}. The active deck was index {}, now it is {}",
    211                             info_prefix,
    212                             self.active_deck,
    213                             self.active_deck - 1
    214                         );
    215                         self.active_deck -= 1
    216                     }
    217                     std::cmp::Ordering::Greater => {
    218                         info!(
    219                             "{}. Active deck remains at index {}.",
    220                             info_prefix, self.active_deck
    221                         )
    222                     }
    223                     std::cmp::Ordering::Equal => {
    224                         if index != 0 {
    225                             info!(
    226                                 "{}. Active deck was index {}, now it is {}",
    227                                 info_prefix,
    228                                 self.active_deck,
    229                                 self.active_deck - 1
    230                             );
    231                             self.active_deck -= 1;
    232                         } else {
    233                             info!(
    234                                 "{}. Active deck remains at index {}.",
    235                                 info_prefix, self.active_deck
    236                             )
    237                         }
    238                     }
    239                 }
    240                 self.removal_request = None;
    241             } else {
    242                 error!("attempted unsucessfully to remove the last deck for this account");
    243             }
    244         } else {
    245             error!("index was out of bounds");
    246         }
    247     }
    248 }
    249 
    250 pub struct Deck {
    251     pub icon: char,
    252     pub name: String,
    253     columns: Columns,
    254 }
    255 
    256 impl Default for Deck {
    257     fn default() -> Self {
    258         let mut columns = Columns::default();
    259         columns.new_column_picker();
    260         Self {
    261             icon: '🇩',
    262             name: String::from("Default Deck"),
    263             columns,
    264         }
    265     }
    266 }
    267 
    268 impl Deck {
    269     pub fn new(icon: char, name: String) -> Self {
    270         let mut columns = Columns::default();
    271         columns.new_column_picker();
    272         Self {
    273             icon,
    274             name,
    275             columns,
    276         }
    277     }
    278 
    279     pub fn new_with_columns(icon: char, name: String, columns: Columns) -> Self {
    280         Self {
    281             icon,
    282             name,
    283             columns,
    284         }
    285     }
    286 
    287     pub fn columns(&self) -> &Columns {
    288         &self.columns
    289     }
    290 
    291     pub fn columns_mut(&mut self) -> &mut Columns {
    292         &mut self.columns
    293     }
    294 
    295     pub fn edit(&mut self, changes: ConfigureDeckResponse) {
    296         self.name = changes.name;
    297         self.icon = changes.icon;
    298     }
    299 }
    300 
    301 pub fn demo_decks(demo_pubkey: Pubkey, ndb: &Ndb) -> Decks {
    302     let deck = {
    303         let mut columns = Columns::default();
    304         columns.add_column(Column::new(vec![
    305             Route::AddColumn(AddColumnRoute::Base),
    306             Route::Accounts(AccountsRoute::Accounts),
    307         ]));
    308 
    309         if let Some(timeline) =
    310             TimelineKind::contact_list(timeline::PubkeySource::Explicit(demo_pubkey))
    311                 .into_timeline(ndb, Some(demo_pubkey.bytes()))
    312         {
    313             columns.add_new_timeline_column(timeline);
    314         }
    315 
    316         columns.add_new_timeline_column(Timeline::hashtag("introductions".to_string()));
    317 
    318         Deck {
    319             icon: '🇩',
    320             name: String::from("Demo Deck"),
    321             columns,
    322         }
    323     };
    324 
    325     Decks::new(deck)
    326 }