AccountData: decouple query from constructor

the ndb query must be as close to the subscription as possible to
avoid events falling through the cracks

Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
kernelkind
2025-07-08 15:09:03 -04:00
parent 6c951d1a29
commit c99b99ed52
6 changed files with 60 additions and 52 deletions

View File

@@ -42,8 +42,8 @@ impl Accounts {
let (mut cache, unknown_id) = AccountCache::new(UserAccount::new( let (mut cache, unknown_id) = AccountCache::new(UserAccount::new(
Keypair::only_pubkey(fallback), Keypair::only_pubkey(fallback),
AccountData { AccountData {
relay: AccountRelayData::new(ndb, txn, fallback.bytes()), relay: AccountRelayData::new(fallback.bytes()),
muted: AccountMutedData::new(ndb, txn, fallback.bytes()), muted: AccountMutedData::new(fallback.bytes()),
}, },
)); ));
@@ -55,7 +55,7 @@ impl Accounts {
match reader.get_accounts() { match reader.get_accounts() {
Ok(accounts) => { Ok(accounts) => {
for account in accounts { for account in accounts {
add_account_from_storage(&mut cache, ndb, txn, account).process_action( add_account_from_storage(&mut cache, account).process_action(
unknown_ids, unknown_ids,
ndb, ndb,
txn, txn,
@@ -75,8 +75,10 @@ impl Accounts {
let relay_defaults = RelayDefaults::new(forced_relays); let relay_defaults = RelayDefaults::new(forced_relays);
let selected = cache.selected(); let selected = cache.selected_mut();
let selected_data = &selected.data; let selected_data = &mut selected.data;
selected_data.query(ndb, txn);
let subs = { let subs = {
AccountSubs::new( AccountSubs::new(
@@ -116,12 +118,7 @@ impl Accounts {
} }
#[must_use = "UnknownIdAction's must be handled. Use .process_unknown_id_action()"] #[must_use = "UnknownIdAction's must be handled. Use .process_unknown_id_action()"]
pub fn add_account( pub fn add_account(&mut self, kp: Keypair) -> Option<AddAccountResponse> {
&mut self,
ndb: &Ndb,
txn: &Transaction,
kp: Keypair,
) -> Option<AddAccountResponse> {
let acc = if let Some(acc) = self.cache.get_mut(&kp.pubkey) { let acc = if let Some(acc) = self.cache.get_mut(&kp.pubkey) {
if kp.secret_key.is_none() || acc.key.secret_key.is_some() { if kp.secret_key.is_none() || acc.key.secret_key.is_some() {
tracing::info!("Already have account, not adding"); tracing::info!("Already have account, not adding");
@@ -132,8 +129,8 @@ impl Accounts {
AccType::Acc(&*acc) AccType::Acc(&*acc)
} else { } else {
let new_account_data = AccountData { let new_account_data = AccountData {
relay: AccountRelayData::new(ndb, txn, kp.pubkey.bytes()), relay: AccountRelayData::new(kp.pubkey.bytes()),
muted: AccountMutedData::new(ndb, txn, kp.pubkey.bytes()), muted: AccountMutedData::new(kp.pubkey.bytes()),
}; };
AccType::Entry( AccType::Entry(
self.cache self.cache
@@ -212,6 +209,7 @@ impl Accounts {
&mut self, &mut self,
pk_to_select: &Pubkey, pk_to_select: &Pubkey,
ndb: &mut Ndb, ndb: &mut Ndb,
txn: &Transaction,
pool: &mut RelayPool, pool: &mut RelayPool,
ctx: &egui::Context, ctx: &egui::Context,
) { ) {
@@ -225,6 +223,7 @@ impl Accounts {
} }
} }
self.get_selected_account_mut().data.query(ndb, txn);
self.subs.swap_to( self.subs.swap_to(
ndb, ndb,
pool, pool,
@@ -336,11 +335,9 @@ fn create_wakeup(ctx: &egui::Context) -> impl Fn() + Send + Sync + Clone + 'stat
fn add_account_from_storage( fn add_account_from_storage(
cache: &mut AccountCache, cache: &mut AccountCache,
ndb: &Ndb,
txn: &Transaction,
user_account_serializable: UserAccountSerializable, user_account_serializable: UserAccountSerializable,
) -> SingleUnkIdAction { ) -> SingleUnkIdAction {
let Some(acc) = get_acc_from_storage(ndb, txn, user_account_serializable) else { let Some(acc) = get_acc_from_storage(user_account_serializable) else {
return SingleUnkIdAction::NoAction; return SingleUnkIdAction::NoAction;
}; };
@@ -350,15 +347,11 @@ fn add_account_from_storage(
SingleUnkIdAction::pubkey(pk) SingleUnkIdAction::pubkey(pk)
} }
fn get_acc_from_storage( fn get_acc_from_storage(user_account_serializable: UserAccountSerializable) -> Option<UserAccount> {
ndb: &Ndb,
txn: &Transaction,
user_account_serializable: UserAccountSerializable,
) -> Option<UserAccount> {
let keypair = user_account_serializable.key; let keypair = user_account_serializable.key;
let new_account_data = AccountData { let new_account_data = AccountData {
relay: AccountRelayData::new(ndb, txn, keypair.pubkey.bytes()), relay: AccountRelayData::new(keypair.pubkey.bytes()),
muted: AccountMutedData::new(ndb, txn, keypair.pubkey.bytes()), muted: AccountMutedData::new(keypair.pubkey.bytes()),
}; };
let mut wallet = None; let mut wallet = None;
@@ -400,6 +393,12 @@ impl AccountData {
resp resp
} }
/// Note: query should be called as close to the subscription as possible
pub(super) fn query(&mut self, ndb: &Ndb, txn: &Transaction) {
self.relay.query(ndb, txn);
self.muted.query(ndb, txn);
}
} }
pub(super) enum AccountDataUpdate { pub(super) enum AccountDataUpdate {

View File

@@ -11,7 +11,7 @@ pub(crate) struct AccountMutedData {
} }
impl AccountMutedData { impl AccountMutedData {
pub fn new(ndb: &Ndb, txn: &Transaction, pubkey: &[u8; 32]) -> Self { pub fn new(pubkey: &[u8; 32]) -> Self {
// Construct a filter for the user's NIP-51 muted list // Construct a filter for the user's NIP-51 muted list
let filter = Filter::new() let filter = Filter::new()
.authors([pubkey]) .authors([pubkey])
@@ -19,21 +19,28 @@ impl AccountMutedData {
.limit(1) .limit(1)
.build(); .build();
AccountMutedData {
filter,
muted: Arc::new(Muted::default()),
}
}
pub(super) fn query(&mut self, ndb: &Ndb, txn: &Transaction) {
// Query the ndb immediately to see if the user's muted list is already there // Query the ndb immediately to see if the user's muted list is already there
let lim = filter.limit().unwrap_or(crate::filter::default_limit()) as i32; let lim = self
.filter
.limit()
.unwrap_or(crate::filter::default_limit()) as i32;
let nks = ndb let nks = ndb
.query(txn, &[filter.clone()], lim) .query(txn, &[self.filter.clone()], lim)
.expect("query user muted results") .expect("query user muted results")
.iter() .iter()
.map(|qr| qr.note_key) .map(|qr| qr.note_key)
.collect::<Vec<NoteKey>>(); .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); debug!("initial muted {:?}", muted);
AccountMutedData { self.muted = Arc::new(muted);
filter,
muted: Arc::new(muted),
}
} }
pub(crate) fn harvest_nip51_muted(ndb: &Ndb, txn: &Transaction, nks: &[NoteKey]) -> Muted { pub(crate) fn harvest_nip51_muted(ndb: &Ndb, txn: &Transaction, nks: &[NoteKey]) -> Muted {

View File

@@ -14,7 +14,7 @@ pub(crate) struct AccountRelayData {
} }
impl AccountRelayData { impl AccountRelayData {
pub fn new(ndb: &Ndb, txn: &Transaction, pubkey: &[u8; 32]) -> Self { pub fn new(pubkey: &[u8; 32]) -> Self {
// Construct a filter for the user's NIP-65 relay list // Construct a filter for the user's NIP-65 relay list
let filter = Filter::new() let filter = Filter::new()
.authors([pubkey]) .authors([pubkey])
@@ -22,26 +22,29 @@ impl AccountRelayData {
.limit(1) .limit(1)
.build(); .build();
AccountRelayData {
filter,
local: BTreeSet::new(),
advertised: BTreeSet::new(),
}
}
pub fn query(&mut self, ndb: &Ndb, txn: &Transaction) {
// Query the ndb immediately to see if the user list is already there // Query the ndb immediately to see if the user list is already there
let lim = filter.limit().unwrap_or(crate::filter::default_limit()) as i32; let lim = self
.filter
.limit()
.unwrap_or(crate::filter::default_limit()) as i32;
let nks = ndb let nks = ndb
.query(txn, &[filter.clone()], lim) .query(txn, &[self.filter.clone()], lim)
.expect("query user relays results") .expect("query user relays results")
.iter() .iter()
.map(|qr| qr.note_key) .map(|qr| qr.note_key)
.collect::<Vec<NoteKey>>(); .collect::<Vec<NoteKey>>();
let relays = Self::harvest_nip65_relays(ndb, txn, &nks); let relays = Self::harvest_nip65_relays(ndb, txn, &nks);
debug!( debug!("initial relays {:?}", relays);
"pubkey {}: initial relays {:?}",
hex::encode(pubkey),
relays
);
AccountRelayData { self.advertised = relays.into_iter().collect()
filter,
local: BTreeSet::new(),
advertised: relays.into_iter().collect(),
}
} }
// standardize the format (ie, trailing slashes) to avoid dups // standardize the format (ie, trailing slashes) to avoid dups

View File

@@ -203,7 +203,7 @@ impl Notedeck {
{ {
for key in &parsed_args.keys { for key in &parsed_args.keys {
info!("adding account: {}", &key.pubkey); info!("adding account: {}", &key.pubkey);
if let Some(resp) = accounts.add_account(&ndb, &txn, key.clone()) { if let Some(resp) = accounts.add_account(key.clone()) {
resp.unk_id_action resp.unk_id_action
.process_action(&mut unknown_ids, &ndb, &txn); .process_action(&mut unknown_ids, &ndb, &txn);
} }
@@ -211,7 +211,7 @@ impl Notedeck {
} }
if let Some(first) = parsed_args.keys.first() { if let Some(first) = parsed_args.keys.first() {
accounts.select_account(&first.pubkey, &mut ndb, &mut pool, ctx); accounts.select_account(&first.pubkey, &mut ndb, &txn, &mut pool, ctx);
} }
let img_cache = Images::new(img_cache_dir); let img_cache = Images::new(img_cache_dir);

View File

@@ -94,7 +94,7 @@ pub fn render_accounts_route(
} }
} }
AccountsRouteResponse::AddAccount(response) => { AccountsRouteResponse::AddAccount(response) => {
let action = process_login_view_response(accounts, decks, col, ndb, response); let action = process_login_view_response(accounts, decks, col, response);
*login_state = Default::default(); *login_state = Default::default();
let router = get_active_columns_mut(accounts, decks) let router = get_active_columns_mut(accounts, decks)
.column_mut(col) .column_mut(col)
@@ -143,20 +143,17 @@ pub fn process_login_view_response(
manager: &mut Accounts, manager: &mut Accounts,
decks: &mut DecksCache, decks: &mut DecksCache,
col: usize, col: usize,
ndb: &Ndb,
response: AccountLoginResponse, response: AccountLoginResponse,
) -> AddAccountAction { ) -> AddAccountAction {
let (r, pubkey) = match response { let (r, pubkey) = match response {
AccountLoginResponse::CreateNew => { AccountLoginResponse::CreateNew => {
let kp = FullKeypair::generate().to_keypair(); let kp = FullKeypair::generate().to_keypair();
let pubkey = kp.pubkey; let pubkey = kp.pubkey;
let txn = Transaction::new(ndb).expect("txn"); (manager.add_account(kp), pubkey)
(manager.add_account(ndb, &txn, kp), pubkey)
} }
AccountLoginResponse::LoginWith(keypair) => { AccountLoginResponse::LoginWith(keypair) => {
let pubkey = keypair.pubkey; let pubkey = keypair.pubkey;
let txn = Transaction::new(ndb).expect("txn"); (manager.add_account(keypair), pubkey)
(manager.add_account(ndb, &txn, keypair), pubkey)
} }
}; };

View File

@@ -78,9 +78,11 @@ impl SwitchingAction {
match &self { match &self {
SwitchingAction::Accounts(account_action) => match account_action { SwitchingAction::Accounts(account_action) => match account_action {
AccountsAction::Switch(switch_action) => { AccountsAction::Switch(switch_action) => {
let txn = Transaction::new(ctx.ndb).expect("txn");
ctx.accounts.select_account( ctx.accounts.select_account(
&switch_action.switch_to, &switch_action.switch_to,
ctx.ndb, ctx.ndb,
&txn,
ctx.pool, ctx.pool,
ui_ctx, ui_ctx,
); );