notedeck

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

decks.rs (9661B)


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