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 }