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 }