notedeck

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

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 }