notedeck

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

commit 329385bd900d98899d9608d5142d63e2153cf1ea
parent a962d67536341311c73296e7510328c2459cc48f
Author: kernelkind <kernelkind@gmail.com>
Date:   Sun, 29 Jun 2025 15:20:47 -0400

move `AcountData` into `UserAccount`

Signed-off-by: kernelkind <kernelkind@gmail.com>

Diffstat:
Mcrates/notedeck/src/account/accounts.rs | 236++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mcrates/notedeck/src/account/mute.rs | 7+++----
Mcrates/notedeck/src/account/relay.rs | 7+++----
Mcrates/notedeck/src/app.rs | 15+++++++++++----
Mcrates/notedeck/src/user_account.rs | 14+++++++++++---
Mcrates/notedeck_columns/src/accounts/mod.rs | 9++++++---
Mcrates/notedeck_columns/src/app.rs | 9++++++---
7 files changed, 161 insertions(+), 136 deletions(-)

diff --git a/crates/notedeck/src/account/accounts.rs b/crates/notedeck/src/account/accounts.rs @@ -1,13 +1,13 @@ -use tracing::{debug, error, info}; +use tracing::{debug, info}; use crate::account::cache::AccountCache; use crate::account::mute::AccountMutedData; use crate::account::relay::{AccountRelayData, RelayDefaults}; use crate::user_account::UserAccountSerializable; -use crate::{AccountStorage, MuteFun, RelaySpec, SingleUnkIdAction, UserAccount}; +use crate::{AccountStorage, MuteFun, RelaySpec, SingleUnkIdAction, UnknownIds, UserAccount}; use enostr::{ClientMessage, FilledKeypair, Keypair, Pubkey, RelayPool}; use nostrdb::{Ndb, Note, Transaction}; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeSet; // TODO: remove this use std::sync::Arc; @@ -17,7 +17,6 @@ use std::sync::Arc; pub struct Accounts { pub cache: AccountCache, key_store: Option<AccountStorage>, - account_data: BTreeMap<[u8; 32], AccountData>, relay_defaults: RelayDefaults, needs_relay_config: bool, } @@ -27,14 +26,29 @@ impl Accounts { key_store: Option<AccountStorage>, forced_relays: Vec<String>, fallback: Pubkey, + ndb: &Ndb, + txn: &Transaction, + unknown_ids: &mut UnknownIds, ) -> Self { - let (mut cache, _) = AccountCache::new(UserAccount::new(Keypair::only_pubkey(fallback))); + let (mut cache, unknown_id) = AccountCache::new(UserAccount::new( + Keypair::only_pubkey(fallback), + AccountData { + relay: AccountRelayData::new(ndb, txn, fallback.bytes()), + muted: AccountMutedData::new(ndb, txn, fallback.bytes()), + }, + )); + + unknown_id.process_action(unknown_ids, ndb, txn); + if let Some(keystore) = &key_store { match keystore.get_accounts() { Ok(accounts) => { for account in accounts { - // TODO(kernelkind): this will get processed in a later commit - let _ = add_account_from_storage(&mut cache, account); + add_account_from_storage(&mut cache, ndb, txn, account).process_action( + unknown_ids, + ndb, + txn, + ) } } Err(e) => { @@ -46,14 +60,11 @@ impl Accounts { } }; - let account_data = BTreeMap::new(); - let relay_defaults = RelayDefaults::new(forced_relays); Accounts { cache, key_store, - account_data, relay_defaults, needs_relay_config: true, } @@ -82,7 +93,12 @@ impl Accounts { } #[must_use = "UnknownIdAction's must be handled. Use .process_unknown_id_action()"] - pub fn add_account(&mut self, kp: Keypair) -> Option<AddAccountResponse> { + pub fn add_account( + &mut self, + ndb: &Ndb, + txn: &Transaction, + kp: Keypair, + ) -> Option<AddAccountResponse> { let acc = if let Some(acc) = self.cache.get_mut(&kp.pubkey) { if kp.secret_key.is_none() || acc.key.secret_key.is_some() { tracing::info!("Already have account, not adding"); @@ -92,7 +108,14 @@ impl Accounts { acc.key = kp.clone(); AccType::Acc(&*acc) } else { - AccType::Entry(self.cache.add(UserAccount::new(kp.clone()))) + let new_account_data = AccountData { + relay: AccountRelayData::new(ndb, txn, kp.pubkey.bytes()), + muted: AccountMutedData::new(ndb, txn, kp.pubkey.bytes()), + }; + AccType::Entry( + self.cache + .add(UserAccount::new(kp.clone(), new_account_data)), + ) }; if let Some(key_store) = &self.key_store { @@ -154,32 +177,35 @@ impl Accounts { self.cache.selected_mut() } - pub fn get_selected_account_data(&mut self) -> Option<&mut AccountData> { - let account_pubkey = *self.selected_account_pubkey_bytes(); - self.account_data.get_mut(&account_pubkey) + fn get_selected_account_data(&self) -> &AccountData { + &self.cache.selected().data + } + + fn get_selected_account_data_mut(&mut self) -> &mut AccountData { + &mut self.cache.selected_mut().data } pub fn select_account(&mut self, pk: &Pubkey) { - if self.cache.select(*pk) { - if let Some(key_store) = &self.key_store { - if let Err(e) = key_store.select_key(Some(*pk)) { - tracing::error!("Could not select key {:?}: {e}", pk); - } + if !self.cache.select(*pk) { + return; + } + + if let Some(key_store) = &self.key_store { + if let Err(e) = key_store.select_key(Some(*pk)) { + tracing::error!("Could not select key {:?}: {e}", pk); } } } pub fn mutefun(&self) -> Box<MuteFun> { - let pubkey = self.cache.selected().key.pubkey.bytes(); - if let Some(account_data) = self.account_data.get(pubkey) { - let muted = Arc::clone(&account_data.muted.muted); - return Box::new(move |note: &Note, thread: &[u8; 32]| muted.is_muted(note, thread)); - } - Box::new(|_: &Note, _: &[u8; 32]| false) + let account_data = self.get_selected_account_data(); + + let muted = Arc::clone(&account_data.muted.muted); + Box::new(move |note: &Note, thread: &[u8; 32]| muted.is_muted(note, thread)) } pub fn send_initial_filters(&mut self, pool: &mut RelayPool, relay_url: &str) { - for data in self.account_data.values() { + for data in (&self.cache).into_iter().map(|(_, acc)| &acc.data) { // send the active account's relay list subscription if let Some(relay_subid) = &data.relay.subid { pool.send_to( @@ -202,49 +228,28 @@ impl Accounts { fn delta_accounts(&self) -> (Vec<[u8; 32]>, Vec<[u8; 32]>) { let mut added = Vec::new(); for pubkey in (&self.cache).into_iter().map(|(pk, _)| pk.bytes()) { - if !self.account_data.contains_key(pubkey) { + if !self.cache.contains(pubkey) { added.push(*pubkey); } } let mut removed = Vec::new(); - for pubkey in self.account_data.keys() { + for (pubkey, _) in &self.cache { if self.cache.get_bytes(pubkey).is_none() { - removed.push(*pubkey); + removed.push(**pubkey); } } (added, removed) } - fn handle_added_account(&mut self, ndb: &Ndb, pubkey: &[u8; 32]) { - debug!("handle_added_account {}", hex::encode(pubkey)); - - // Create the user account data - let new_account_data = AccountData { - relay: AccountRelayData::new(ndb, pubkey), - muted: AccountMutedData::new(ndb, pubkey), - }; - self.account_data.insert(*pubkey, new_account_data); - } - - fn handle_removed_account(&mut self, pubkey: &[u8; 32]) { - debug!("handle_removed_account {}", hex::encode(pubkey)); - // FIXME - we need to unsubscribe here - self.account_data.remove(pubkey); - } - fn poll_for_updates(&mut self, ndb: &Ndb) -> bool { let mut changed = false; - for (pubkey, data) in &mut self.account_data { + for (pubkey, data) in &mut self.cache.iter_mut().map(|(pk, a)| (pk, &mut a.data)) { if let Some(sub) = data.relay.sub { let nks = ndb.poll_for_notes(sub, 1); if !nks.is_empty() { let txn = Transaction::new(ndb).expect("txn"); let relays = AccountRelayData::harvest_nip65_relays(ndb, &txn, &nks); - debug!( - "pubkey {}: updated relays {:?}", - hex::encode(pubkey), - relays - ); + debug!("pubkey {}: updated relays {:?}", pubkey.hex(), relays); data.relay.advertised = relays.into_iter().collect(); changed = true; } @@ -254,7 +259,7 @@ impl Accounts { if !nks.is_empty() { let txn = Transaction::new(ndb).expect("txn"); let muted = AccountMutedData::harvest_nip51_muted(ndb, &txn, &nks); - debug!("pubkey {}: updated muted {:?}", hex::encode(pubkey), muted); + debug!("pubkey {}: updated muted {:?}", pubkey.hex(), muted); data.muted.muted = Arc::new(muted); changed = true; } @@ -278,10 +283,9 @@ impl Accounts { // Compose the desired relay lists from the selected account if desired_relays.is_empty() { - if let Some(data) = self.get_selected_account_data() { - desired_relays.extend(data.relay.local.iter().cloned()); - desired_relays.extend(data.relay.advertised.iter().cloned()); - } + let data = self.get_selected_account_data_mut(); + desired_relays.extend(data.relay.local.iter().cloned()); + desired_relays.extend(data.relay.advertised.iter().cloned()); } // If no relays are specified at this point use the bootstrap list @@ -331,32 +335,28 @@ impl Accounts { // Do we need to deactivate any existing account subs? - let selected = self.cache.selected(); + let selected = self.cache.selected().key.pubkey; - for (pk, account) in &self.cache { - if *pk != selected.key.pubkey { - // this account is not currently selected - if let Some(data) = self.account_data.get_mut(account.key.pubkey.bytes()) { - if data.relay.sub.is_some() { - // this account has relay subs, deactivate them - data.relay.deactivate(ndb, pool); - } - if data.muted.sub.is_some() { - // this account has muted subs, deactivate them - data.muted.deactivate(ndb, pool); - } - } + for (pk, account) in &mut self.cache.iter_mut() { + if *pk == selected { + continue; + } + + let data = &mut account.data; + // this account is not currently selected + if data.relay.sub.is_some() { + // this account has relay subs, deactivate them + data.relay.deactivate(ndb, pool); + } + if data.muted.sub.is_some() { + // this account has muted subs, deactivate them + data.muted.deactivate(ndb, pool); } } // Were any accounts added or removed? let (added, removed) = self.delta_accounts(); - for pk in added { - self.handle_added_account(ndb, &pk); - need_reconfig = true; - } - for pk in removed { - self.handle_removed_account(&pk); + if !added.is_empty() || !removed.is_empty() { need_reconfig = true; } @@ -370,15 +370,14 @@ impl Accounts { } // Do we need to activate account subs? - if let Some(data) = self.get_selected_account_data() { - if data.relay.sub.is_none() { - // the currently selected account doesn't have relay subs, activate them - data.relay.activate(ndb, pool); - } - if data.muted.sub.is_none() { - // the currently selected account doesn't have muted subs, activate them - data.muted.activate(ndb, pool); - } + let data = self.get_selected_account_data_mut(); + if data.relay.sub.is_none() { + // the currently selected account doesn't have relay subs, activate them + data.relay.activate(ndb, pool); + } + if data.muted.sub.is_none() { + // the currently selected account doesn't have muted subs, activate them + data.muted.activate(ndb, pool); } } @@ -398,35 +397,31 @@ impl Accounts { RelayAction::Remove => info!("remove advertised relay \"{}\"", relay_url), } - let selected = self.cache.selected(); - let key_bytes: [u8; 32] = *self.cache.selected().key.pubkey.bytes(); - match self.account_data.get_mut(&key_bytes) { - None => error!("no account data found for the provided key."), - Some(account_data) => { - let advertised = &mut account_data.relay.advertised; - if advertised.is_empty() { - // If the selected account has no advertised relays, - // initialize with the bootstrapping set. - advertised.extend(self.relay_defaults.bootstrap_relays.iter().cloned()); - } - match action { - RelayAction::Add => { - advertised.insert(RelaySpec::new(relay_url, false, false)); - } - RelayAction::Remove => { - advertised.remove(&RelaySpec::new(relay_url, false, false)); - } - } - self.needs_relay_config = true; + let selected = self.cache.selected_mut(); + let account_data = &mut selected.data; - // If we have the secret key publish the NIP-65 relay list - if let Some(secretkey) = &selected.key.secret_key { - account_data - .relay - .publish_nip65_relays(&secretkey.to_secret_bytes(), pool); - } + let advertised = &mut account_data.relay.advertised; + if advertised.is_empty() { + // If the selected account has no advertised relays, + // initialize with the bootstrapping set. + advertised.extend(self.relay_defaults.bootstrap_relays.iter().cloned()); + } + match action { + RelayAction::Add => { + advertised.insert(RelaySpec::new(relay_url, false, false)); + } + RelayAction::Remove => { + advertised.remove(&RelaySpec::new(relay_url, false, false)); } } + self.needs_relay_config = true; + + // If we have the secret key publish the NIP-65 relay list + if let Some(secretkey) = &selected.key.secret_key { + account_data + .relay + .publish_nip65_relays(&secretkey.to_secret_bytes(), pool); + } } pub fn add_advertised_relay(&mut self, relay_to_add: &str, pool: &mut RelayPool) { @@ -454,9 +449,11 @@ impl<'a> AccType<'a> { fn add_account_from_storage( cache: &mut AccountCache, + ndb: &Ndb, + txn: &Transaction, user_account_serializable: UserAccountSerializable, ) -> SingleUnkIdAction { - let Some(acc) = get_acc_from_storage(user_account_serializable) else { + let Some(acc) = get_acc_from_storage(ndb, txn, user_account_serializable) else { return SingleUnkIdAction::NoAction; }; @@ -466,8 +463,16 @@ fn add_account_from_storage( SingleUnkIdAction::pubkey(pk) } -fn get_acc_from_storage(user_account_serializable: UserAccountSerializable) -> Option<UserAccount> { +fn get_acc_from_storage( + ndb: &Ndb, + txn: &Transaction, + user_account_serializable: UserAccountSerializable, +) -> Option<UserAccount> { let keypair = user_account_serializable.key; + let new_account_data = AccountData { + relay: AccountRelayData::new(ndb, txn, keypair.pubkey.bytes()), + muted: AccountMutedData::new(ndb, txn, keypair.pubkey.bytes()), + }; let mut wallet = None; if let Some(wallet_s) = user_account_serializable.wallet { @@ -483,6 +488,7 @@ fn get_acc_from_storage(user_account_serializable: UserAccountSerializable) -> O Some(UserAccount { key: keypair, wallet, + data: new_account_data, }) } diff --git a/crates/notedeck/src/account/mute.rs b/crates/notedeck/src/account/mute.rs @@ -15,7 +15,7 @@ pub(crate) struct AccountMutedData { } impl AccountMutedData { - pub fn new(ndb: &Ndb, pubkey: &[u8; 32]) -> Self { + pub fn new(ndb: &Ndb, txn: &Transaction, pubkey: &[u8; 32]) -> Self { // Construct a filter for the user's NIP-51 muted list let filter = Filter::new() .authors([pubkey]) @@ -24,15 +24,14 @@ impl AccountMutedData { .build(); // Query the ndb immediately to see if the user's muted list is already there - let txn = Transaction::new(ndb).expect("transaction"); let lim = filter.limit().unwrap_or(crate::filter::default_limit()) as i32; let nks = ndb - .query(&txn, &[filter.clone()], lim) + .query(txn, &[filter.clone()], lim) .expect("query user muted results") .iter() .map(|qr| qr.note_key) .collect::<Vec<NoteKey>>(); - let muted = Self::harvest_nip51_muted(ndb, &txn, &nks); + let muted = Self::harvest_nip51_muted(ndb, txn, &nks); debug!("pubkey {}: initial muted {:?}", hex::encode(pubkey), muted); AccountMutedData { diff --git a/crates/notedeck/src/account/relay.rs b/crates/notedeck/src/account/relay.rs @@ -17,7 +17,7 @@ pub(crate) struct AccountRelayData { } impl AccountRelayData { - pub fn new(ndb: &Ndb, pubkey: &[u8; 32]) -> Self { + pub fn new(ndb: &Ndb, txn: &Transaction, pubkey: &[u8; 32]) -> Self { // Construct a filter for the user's NIP-65 relay list let filter = Filter::new() .authors([pubkey]) @@ -26,15 +26,14 @@ impl AccountRelayData { .build(); // Query the ndb immediately to see if the user list is already there - let txn = Transaction::new(ndb).expect("transaction"); let lim = filter.limit().unwrap_or(crate::filter::default_limit()) as i32; let nks = ndb - .query(&txn, &[filter.clone()], lim) + .query(txn, &[filter.clone()], lim) .expect("query user relays results") .iter() .map(|qr| qr.note_key) .collect::<Vec<NoteKey>>(); - let relays = Self::harvest_nip65_relays(ndb, &txn, &nks); + let relays = Self::harvest_nip65_relays(ndb, txn, &nks); debug!( "pubkey {}: initial relays {:?}", hex::encode(pubkey), diff --git a/crates/notedeck/src/app.rs b/crates/notedeck/src/app.rs @@ -176,16 +176,23 @@ impl Notedeck { None }; - let mut accounts = Accounts::new(keystore, parsed_args.relays.clone(), FALLBACK_PUBKEY()); - let mut unknown_ids = UnknownIds::default(); let ndb = Ndb::new(&dbpath_str, &config).expect("ndb"); + let txn = Transaction::new(&ndb).expect("txn"); + + let mut accounts = Accounts::new( + keystore, + parsed_args.relays.clone(), + FALLBACK_PUBKEY(), + &ndb, + &txn, + &mut unknown_ids, + ); { - let txn = Transaction::new(&ndb).expect("txn"); for key in &parsed_args.keys { info!("adding account: {}", &key.pubkey); - if let Some(resp) = accounts.add_account(key.clone()) { + if let Some(resp) = accounts.add_account(&ndb, &txn, key.clone()) { resp.unk_id_action .process_action(&mut unknown_ids, &ndb, &txn); } diff --git a/crates/notedeck/src/user_account.rs b/crates/notedeck/src/user_account.rs @@ -1,16 +1,24 @@ use enostr::{Keypair, KeypairUnowned}; use tokenator::{ParseError, TokenParser, TokenSerializable}; -use crate::wallet::{WalletSerializable, ZapWallet}; +use crate::{ + wallet::{WalletSerializable, ZapWallet}, + AccountData, +}; pub struct UserAccount { pub key: Keypair, pub wallet: Option<ZapWallet>, + pub data: AccountData, } impl UserAccount { - pub fn new(key: Keypair) -> Self { - Self { key, wallet: None } + pub fn new(key: Keypair, data: AccountData) -> Self { + Self { + key, + wallet: None, + data, + } } pub fn keypair(&self) -> KeypairUnowned { diff --git a/crates/notedeck_columns/src/accounts/mod.rs b/crates/notedeck_columns/src/accounts/mod.rs @@ -94,7 +94,7 @@ pub fn render_accounts_route( } } AccountsRouteResponse::AddAccount(response) => { - let action = process_login_view_response(accounts, decks, col, response); + let action = process_login_view_response(accounts, decks, col, ndb, response); *login_state = Default::default(); let router = get_active_columns_mut(accounts, decks) .column_mut(col) @@ -144,17 +144,20 @@ pub fn process_login_view_response( manager: &mut Accounts, decks: &mut DecksCache, col: usize, + ndb: &Ndb, response: AccountLoginResponse, ) -> AddAccountAction { let (r, pubkey) = match response { AccountLoginResponse::CreateNew => { let kp = FullKeypair::generate().to_keypair(); let pubkey = kp.pubkey; - (manager.add_account(kp), pubkey) + let txn = Transaction::new(ndb).expect("txn"); + (manager.add_account(ndb, &txn, kp), pubkey) } AccountLoginResponse::LoginWith(keypair) => { let pubkey = keypair.pubkey; - (manager.add_account(keypair), pubkey) + let txn = Transaction::new(ndb).expect("txn"); + (manager.add_account(ndb, &txn, keypair), pubkey) } }; diff --git a/crates/notedeck_columns/src/app.rs b/crates/notedeck_columns/src/app.rs @@ -770,9 +770,12 @@ pub fn set_demo( ) { let fallback = decks_cache.get_fallback_pubkey(); let txn = Transaction::new(ndb).expect("txn"); - if let Some(resp) = - accounts.add_account(Keypair::only_pubkey(*decks_cache.get_fallback_pubkey())) - { + if let Some(resp) = accounts.add_account( + ndb, + &txn, + Keypair::only_pubkey(*decks_cache.get_fallback_pubkey()), + ) { + let txn = Transaction::new(ndb).expect("txn"); resp.unk_id_action.process_action(unk_ids, ndb, &txn); } accounts.select_account(fallback);