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