Another massive refactor to change the way routing works. Now any column can route anywhere. Also things are generally just much better and more modular via the new struct split borrowing technique. I didn't even try to split this into smaller commits for my sanity. Signed-off-by: William Casarin <jb55@jb55.com>
211 lines
6.0 KiB
Rust
211 lines
6.0 KiB
Rust
use std::cmp::Ordering;
|
|
|
|
use enostr::{FilledKeypair, FullKeypair, Keypair};
|
|
use nostrdb::Ndb;
|
|
|
|
use crate::{
|
|
column::Columns,
|
|
imgcache::ImageCache,
|
|
key_storage::{KeyStorage, KeyStorageResponse, KeyStorageType},
|
|
login_manager::LoginState,
|
|
route::{Route, Router},
|
|
ui::{
|
|
account_login_view::{AccountLoginResponse, AccountLoginView},
|
|
account_management::{AccountsView, AccountsViewResponse},
|
|
},
|
|
};
|
|
use tracing::info;
|
|
|
|
pub use crate::user_account::UserAccount;
|
|
|
|
/// The interface for managing the user's accounts.
|
|
/// Represents all user-facing operations related to account management.
|
|
pub struct AccountManager {
|
|
currently_selected_account: Option<usize>,
|
|
accounts: Vec<UserAccount>,
|
|
key_store: KeyStorageType,
|
|
}
|
|
|
|
// TODO(jb55): move to accounts/route.rs
|
|
pub enum AccountsRouteResponse {
|
|
Accounts(AccountsViewResponse),
|
|
AddAccount(AccountLoginResponse),
|
|
}
|
|
|
|
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
|
|
pub enum AccountsRoute {
|
|
Accounts,
|
|
AddAccount,
|
|
}
|
|
|
|
/// Render account management views from a route
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn render_accounts_route(
|
|
ui: &mut egui::Ui,
|
|
ndb: &Ndb,
|
|
col: usize,
|
|
columns: &mut Columns,
|
|
img_cache: &mut ImageCache,
|
|
accounts: &mut AccountManager,
|
|
login_state: &mut LoginState,
|
|
route: AccountsRoute,
|
|
) {
|
|
let router = columns.column_mut(col).router_mut();
|
|
let resp = match route {
|
|
AccountsRoute::Accounts => AccountsView::new(ndb, accounts, img_cache)
|
|
.ui(ui)
|
|
.inner
|
|
.map(AccountsRouteResponse::Accounts),
|
|
|
|
AccountsRoute::AddAccount => AccountLoginView::new(login_state)
|
|
.ui(ui)
|
|
.inner
|
|
.map(AccountsRouteResponse::AddAccount),
|
|
};
|
|
|
|
if let Some(resp) = resp {
|
|
match resp {
|
|
AccountsRouteResponse::Accounts(response) => {
|
|
process_accounts_view_response(accounts, response, router);
|
|
}
|
|
AccountsRouteResponse::AddAccount(response) => {
|
|
process_login_view_response(accounts, response);
|
|
*login_state = Default::default();
|
|
router.go_back();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn process_accounts_view_response(
|
|
manager: &mut AccountManager,
|
|
response: AccountsViewResponse,
|
|
router: &mut Router<Route>,
|
|
) {
|
|
match response {
|
|
AccountsViewResponse::RemoveAccount(index) => {
|
|
manager.remove_account(index);
|
|
}
|
|
AccountsViewResponse::SelectAccount(index) => {
|
|
manager.select_account(index);
|
|
}
|
|
AccountsViewResponse::RouteToLogin => {
|
|
router.route_to(Route::add_account());
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AccountManager {
|
|
pub fn new(currently_selected_account: Option<usize>, key_store: KeyStorageType) -> Self {
|
|
let accounts = if let KeyStorageResponse::ReceivedResult(res) = key_store.get_keys() {
|
|
res.unwrap_or_default()
|
|
} else {
|
|
Vec::new()
|
|
};
|
|
|
|
AccountManager {
|
|
currently_selected_account,
|
|
accounts,
|
|
key_store,
|
|
}
|
|
}
|
|
|
|
pub fn get_accounts(&self) -> &Vec<UserAccount> {
|
|
&self.accounts
|
|
}
|
|
|
|
pub fn get_account(&self, ind: usize) -> Option<&UserAccount> {
|
|
self.accounts.get(ind)
|
|
}
|
|
|
|
pub fn find_account(&self, pk: &[u8; 32]) -> Option<&UserAccount> {
|
|
self.accounts.iter().find(|acc| acc.pubkey.bytes() == pk)
|
|
}
|
|
|
|
pub fn remove_account(&mut self, index: usize) {
|
|
if let Some(account) = self.accounts.get(index) {
|
|
let _ = self.key_store.remove_key(account);
|
|
self.accounts.remove(index);
|
|
|
|
if let Some(selected_index) = self.currently_selected_account {
|
|
match selected_index.cmp(&index) {
|
|
Ordering::Greater => {
|
|
self.select_account(selected_index - 1);
|
|
}
|
|
Ordering::Equal => {
|
|
self.clear_selected_account();
|
|
}
|
|
Ordering::Less => {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn has_account_pubkey(&self, pubkey: &[u8; 32]) -> bool {
|
|
for account in &self.accounts {
|
|
if account.pubkey.bytes() == pubkey {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
false
|
|
}
|
|
|
|
pub fn add_account(&mut self, account: Keypair) -> bool {
|
|
if self.has_account_pubkey(account.pubkey.bytes()) {
|
|
info!("already have account, not adding {}", account.pubkey);
|
|
return false;
|
|
}
|
|
let _ = self.key_store.add_key(&account);
|
|
self.accounts.push(account);
|
|
true
|
|
}
|
|
|
|
pub fn num_accounts(&self) -> usize {
|
|
self.accounts.len()
|
|
}
|
|
|
|
pub fn get_selected_account_index(&self) -> Option<usize> {
|
|
self.currently_selected_account
|
|
}
|
|
|
|
pub fn selected_or_first_nsec(&self) -> Option<FilledKeypair<'_>> {
|
|
self.get_selected_account()
|
|
.and_then(|kp| kp.to_full())
|
|
.or_else(|| self.accounts.iter().find_map(|a| a.to_full()))
|
|
}
|
|
|
|
pub fn get_selected_account(&self) -> Option<&UserAccount> {
|
|
if let Some(account_index) = self.currently_selected_account {
|
|
if let Some(account) = self.get_account(account_index) {
|
|
Some(account)
|
|
} else {
|
|
None
|
|
}
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn select_account(&mut self, index: usize) {
|
|
if self.accounts.get(index).is_some() {
|
|
self.currently_selected_account = Some(index)
|
|
}
|
|
}
|
|
|
|
pub fn clear_selected_account(&mut self) {
|
|
self.currently_selected_account = None
|
|
}
|
|
}
|
|
|
|
pub fn process_login_view_response(manager: &mut AccountManager, response: AccountLoginResponse) {
|
|
match response {
|
|
AccountLoginResponse::CreateNew => {
|
|
manager.add_account(FullKeypair::generate().to_keypair());
|
|
}
|
|
AccountLoginResponse::LoginWith(keypair) => {
|
|
manager.add_account(keypair);
|
|
}
|
|
}
|
|
}
|