account_manager.rs (6125B)
1 use std::cmp::Ordering; 2 3 use enostr::{FilledKeypair, FullKeypair, Keypair}; 4 use nostrdb::Ndb; 5 6 use crate::{ 7 column::Columns, 8 imgcache::ImageCache, 9 key_storage::{KeyStorage, KeyStorageResponse, KeyStorageType}, 10 login_manager::LoginState, 11 route::{Route, Router}, 12 ui::{ 13 account_login_view::{AccountLoginResponse, AccountLoginView}, 14 account_management::{AccountsView, AccountsViewResponse}, 15 }, 16 }; 17 use tracing::info; 18 19 pub use crate::user_account::UserAccount; 20 21 /// The interface for managing the user's accounts. 22 /// Represents all user-facing operations related to account management. 23 pub struct AccountManager { 24 currently_selected_account: Option<usize>, 25 accounts: Vec<UserAccount>, 26 key_store: KeyStorageType, 27 } 28 29 // TODO(jb55): move to accounts/route.rs 30 pub enum AccountsRouteResponse { 31 Accounts(AccountsViewResponse), 32 AddAccount(AccountLoginResponse), 33 } 34 35 #[derive(Debug, Eq, PartialEq, Clone, Copy)] 36 pub enum AccountsRoute { 37 Accounts, 38 AddAccount, 39 } 40 41 /// Render account management views from a route 42 #[allow(clippy::too_many_arguments)] 43 pub fn render_accounts_route( 44 ui: &mut egui::Ui, 45 ndb: &Ndb, 46 col: usize, 47 columns: &mut Columns, 48 img_cache: &mut ImageCache, 49 accounts: &mut AccountManager, 50 login_state: &mut LoginState, 51 route: AccountsRoute, 52 ) { 53 let router = columns.column_mut(col).router_mut(); 54 let resp = match route { 55 AccountsRoute::Accounts => AccountsView::new(ndb, accounts, img_cache) 56 .ui(ui) 57 .inner 58 .map(AccountsRouteResponse::Accounts), 59 60 AccountsRoute::AddAccount => AccountLoginView::new(login_state) 61 .ui(ui) 62 .inner 63 .map(AccountsRouteResponse::AddAccount), 64 }; 65 66 if let Some(resp) = resp { 67 match resp { 68 AccountsRouteResponse::Accounts(response) => { 69 process_accounts_view_response(accounts, response, router); 70 } 71 AccountsRouteResponse::AddAccount(response) => { 72 process_login_view_response(accounts, response); 73 *login_state = Default::default(); 74 router.go_back(); 75 } 76 } 77 } 78 } 79 80 pub fn process_accounts_view_response( 81 manager: &mut AccountManager, 82 response: AccountsViewResponse, 83 router: &mut Router<Route>, 84 ) { 85 match response { 86 AccountsViewResponse::RemoveAccount(index) => { 87 manager.remove_account(index); 88 } 89 AccountsViewResponse::SelectAccount(index) => { 90 manager.select_account(index); 91 } 92 AccountsViewResponse::RouteToLogin => { 93 router.route_to(Route::add_account()); 94 } 95 } 96 } 97 98 impl AccountManager { 99 pub fn new(currently_selected_account: Option<usize>, key_store: KeyStorageType) -> Self { 100 let accounts = if let KeyStorageResponse::ReceivedResult(res) = key_store.get_keys() { 101 res.unwrap_or_default() 102 } else { 103 Vec::new() 104 }; 105 106 AccountManager { 107 currently_selected_account, 108 accounts, 109 key_store, 110 } 111 } 112 113 pub fn get_accounts(&self) -> &Vec<UserAccount> { 114 &self.accounts 115 } 116 117 pub fn get_account(&self, ind: usize) -> Option<&UserAccount> { 118 self.accounts.get(ind) 119 } 120 121 pub fn find_account(&self, pk: &[u8; 32]) -> Option<&UserAccount> { 122 self.accounts.iter().find(|acc| acc.pubkey.bytes() == pk) 123 } 124 125 pub fn remove_account(&mut self, index: usize) { 126 if let Some(account) = self.accounts.get(index) { 127 let _ = self.key_store.remove_key(account); 128 self.accounts.remove(index); 129 130 if let Some(selected_index) = self.currently_selected_account { 131 match selected_index.cmp(&index) { 132 Ordering::Greater => { 133 self.select_account(selected_index - 1); 134 } 135 Ordering::Equal => { 136 self.clear_selected_account(); 137 } 138 Ordering::Less => {} 139 } 140 } 141 } 142 } 143 144 pub fn has_account_pubkey(&self, pubkey: &[u8; 32]) -> bool { 145 for account in &self.accounts { 146 if account.pubkey.bytes() == pubkey { 147 return true; 148 } 149 } 150 151 false 152 } 153 154 pub fn add_account(&mut self, account: Keypair) -> bool { 155 if self.has_account_pubkey(account.pubkey.bytes()) { 156 info!("already have account, not adding {}", account.pubkey); 157 return false; 158 } 159 let _ = self.key_store.add_key(&account); 160 self.accounts.push(account); 161 true 162 } 163 164 pub fn num_accounts(&self) -> usize { 165 self.accounts.len() 166 } 167 168 pub fn get_selected_account_index(&self) -> Option<usize> { 169 self.currently_selected_account 170 } 171 172 pub fn selected_or_first_nsec(&self) -> Option<FilledKeypair<'_>> { 173 self.get_selected_account() 174 .and_then(|kp| kp.to_full()) 175 .or_else(|| self.accounts.iter().find_map(|a| a.to_full())) 176 } 177 178 pub fn get_selected_account(&self) -> Option<&UserAccount> { 179 if let Some(account_index) = self.currently_selected_account { 180 if let Some(account) = self.get_account(account_index) { 181 Some(account) 182 } else { 183 None 184 } 185 } else { 186 None 187 } 188 } 189 190 pub fn select_account(&mut self, index: usize) { 191 if self.accounts.get(index).is_some() { 192 self.currently_selected_account = Some(index) 193 } 194 } 195 196 pub fn clear_selected_account(&mut self) { 197 self.currently_selected_account = None 198 } 199 } 200 201 pub fn process_login_view_response(manager: &mut AccountManager, response: AccountLoginResponse) { 202 match response { 203 AccountLoginResponse::CreateNew => { 204 manager.add_account(FullKeypair::generate().to_keypair()); 205 } 206 AccountLoginResponse::LoginWith(keypair) => { 207 manager.add_account(keypair); 208 } 209 } 210 }