notedeck

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

commit e8d833bf891d60fc43d4a3805551ef9aebe42d88
parent 10ed593b6dbfcf65136c56a49e12e79cf087f4f3
Author: kernelkind <kernelkind@gmail.com>
Date:   Wed, 25 Jun 2025 16:24:27 -0400

accounts: move mute stuff to own module

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

Diffstat:
Mcrates/notedeck/src/account/accounts.rs | 122++-----------------------------------------------------------------------------
Mcrates/notedeck/src/account/mod.rs | 1+
Acrates/notedeck/src/account/mute.rs | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 126 insertions(+), 119 deletions(-)

diff --git a/crates/notedeck/src/account/accounts.rs b/crates/notedeck/src/account/accounts.rs @@ -1,14 +1,12 @@ use tracing::{debug, error, info}; +use crate::account::mute::AccountMutedData; use crate::account::relay::AccountRelayData; -use crate::{ - AccountStorage, MuteFun, Muted, RelaySpec, SingleUnkIdAction, UnknownIds, UserAccount, -}; +use crate::{AccountStorage, MuteFun, RelaySpec, SingleUnkIdAction, UnknownIds, UserAccount}; use enostr::{ClientMessage, FilledKeypair, Keypair, Pubkey, RelayPool}; -use nostrdb::{Filter, Ndb, Note, NoteKey, Subscription, Transaction}; +use nostrdb::{Ndb, Note, Transaction}; use std::cmp::Ordering; use std::collections::{BTreeMap, BTreeSet}; -use uuid::Uuid; // TODO: remove this use std::sync::Arc; @@ -46,120 +44,6 @@ pub struct AddAccountAction { pub unk_id_action: SingleUnkIdAction, } -pub struct AccountMutedData { - filter: Filter, - subid: Option<String>, - sub: Option<Subscription>, - muted: Arc<Muted>, -} - -impl AccountMutedData { - pub fn new(ndb: &Ndb, pubkey: &[u8; 32]) -> Self { - // Construct a filter for the user's NIP-51 muted list - let filter = Filter::new() - .authors([pubkey]) - .kinds([10000]) - .limit(1) - .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) - .expect("query user muted results") - .iter() - .map(|qr| qr.note_key) - .collect::<Vec<NoteKey>>(); - let muted = Self::harvest_nip51_muted(ndb, &txn, &nks); - debug!("pubkey {}: initial muted {:?}", hex::encode(pubkey), muted); - - AccountMutedData { - filter, - subid: None, - sub: None, - muted: Arc::new(muted), - } - } - - // make this account the current selected account - pub fn activate(&mut self, ndb: &Ndb, pool: &mut RelayPool) { - debug!("activating muted sub {}", self.filter.json().unwrap()); - assert_eq!(self.subid, None, "subid already exists"); - assert_eq!(self.sub, None, "sub already exists"); - - // local subscription - let sub = ndb - .subscribe(&[self.filter.clone()]) - .expect("ndb muted subscription"); - - // remote subscription - let subid = Uuid::new_v4().to_string(); - pool.subscribe(subid.clone(), vec![self.filter.clone()]); - - self.sub = Some(sub); - self.subid = Some(subid); - } - - // this account is no longer the selected account - pub fn deactivate(&mut self, ndb: &mut Ndb, pool: &mut RelayPool) { - debug!("deactivating muted sub {}", self.filter.json().unwrap()); - assert_ne!(self.subid, None, "subid doesn't exist"); - assert_ne!(self.sub, None, "sub doesn't exist"); - - // remote subscription - pool.unsubscribe(self.subid.as_ref().unwrap().clone()); - - // local subscription - ndb.unsubscribe(self.sub.unwrap()) - .expect("ndb muted unsubscribe"); - - self.sub = None; - self.subid = None; - } - - fn harvest_nip51_muted(ndb: &Ndb, txn: &Transaction, nks: &[NoteKey]) -> Muted { - let mut muted = Muted::default(); - for nk in nks.iter() { - if let Ok(note) = ndb.get_note_by_key(txn, *nk) { - for tag in note.tags() { - match tag.get(0).and_then(|t| t.variant().str()) { - Some("p") => { - if let Some(id) = tag.get(1).and_then(|f| f.variant().id()) { - muted.pubkeys.insert(*id); - } - } - Some("t") => { - if let Some(str) = tag.get(1).and_then(|f| f.variant().str()) { - muted.hashtags.insert(str.to_string()); - } - } - Some("word") => { - if let Some(str) = tag.get(1).and_then(|f| f.variant().str()) { - muted.words.insert(str.to_string()); - } - } - Some("e") => { - if let Some(id) = tag.get(1).and_then(|f| f.variant().id()) { - muted.threads.insert(*id); - } - } - Some("alt") => { - // maybe we can ignore these? - } - Some(x) => error!("query_nip51_muted: unexpected tag: {}", x), - None => error!( - "query_nip51_muted: bad tag value: {:?}", - tag.get_unchecked(0).variant() - ), - } - } - } - } - muted - } -} - pub struct AccountData { pub(crate) relay: AccountRelayData, pub(crate) muted: AccountMutedData, diff --git a/crates/notedeck/src/account/mod.rs b/crates/notedeck/src/account/mod.rs @@ -1,2 +1,3 @@ pub mod accounts; +pub mod mute; pub mod relay; diff --git a/crates/notedeck/src/account/mute.rs b/crates/notedeck/src/account/mute.rs @@ -0,0 +1,122 @@ +use std::sync::Arc; + +use enostr::RelayPool; +use nostrdb::{Filter, Ndb, NoteKey, Subscription, Transaction}; +use tracing::{debug, error}; +use uuid::Uuid; + +use crate::Muted; + +pub(crate) struct AccountMutedData { + pub filter: Filter, + pub subid: Option<String>, + pub sub: Option<Subscription>, + pub muted: Arc<Muted>, +} + +impl AccountMutedData { + pub fn new(ndb: &Ndb, pubkey: &[u8; 32]) -> Self { + // Construct a filter for the user's NIP-51 muted list + let filter = Filter::new() + .authors([pubkey]) + .kinds([10000]) + .limit(1) + .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) + .expect("query user muted results") + .iter() + .map(|qr| qr.note_key) + .collect::<Vec<NoteKey>>(); + let muted = Self::harvest_nip51_muted(ndb, &txn, &nks); + debug!("pubkey {}: initial muted {:?}", hex::encode(pubkey), muted); + + AccountMutedData { + filter, + subid: None, + sub: None, + muted: Arc::new(muted), + } + } + + // make this account the current selected account + pub fn activate(&mut self, ndb: &Ndb, pool: &mut RelayPool) { + debug!("activating muted sub {}", self.filter.json().unwrap()); + assert_eq!(self.subid, None, "subid already exists"); + assert_eq!(self.sub, None, "sub already exists"); + + // local subscription + let sub = ndb + .subscribe(&[self.filter.clone()]) + .expect("ndb muted subscription"); + + // remote subscription + let subid = Uuid::new_v4().to_string(); + pool.subscribe(subid.clone(), vec![self.filter.clone()]); + + self.sub = Some(sub); + self.subid = Some(subid); + } + + // this account is no longer the selected account + pub fn deactivate(&mut self, ndb: &mut Ndb, pool: &mut RelayPool) { + debug!("deactivating muted sub {}", self.filter.json().unwrap()); + assert_ne!(self.subid, None, "subid doesn't exist"); + assert_ne!(self.sub, None, "sub doesn't exist"); + + // remote subscription + pool.unsubscribe(self.subid.as_ref().unwrap().clone()); + + // local subscription + ndb.unsubscribe(self.sub.unwrap()) + .expect("ndb muted unsubscribe"); + + self.sub = None; + self.subid = None; + } + + pub(crate) fn harvest_nip51_muted(ndb: &Ndb, txn: &Transaction, nks: &[NoteKey]) -> Muted { + let mut muted = Muted::default(); + for nk in nks.iter() { + if let Ok(note) = ndb.get_note_by_key(txn, *nk) { + for tag in note.tags() { + match tag.get(0).and_then(|t| t.variant().str()) { + Some("p") => { + if let Some(id) = tag.get(1).and_then(|f| f.variant().id()) { + muted.pubkeys.insert(*id); + } + } + Some("t") => { + if let Some(str) = tag.get(1).and_then(|f| f.variant().str()) { + muted.hashtags.insert(str.to_string()); + } + } + Some("word") => { + if let Some(str) = tag.get(1).and_then(|f| f.variant().str()) { + muted.words.insert(str.to_string()); + } + } + Some("e") => { + if let Some(id) = tag.get(1).and_then(|f| f.variant().id()) { + muted.threads.insert(*id); + } + } + Some("alt") => { + // maybe we can ignore these? + } + Some(x) => error!("query_nip51_muted: unexpected tag: {}", x), + None => error!( + "query_nip51_muted: bad tag value: {:?}", + tag.get_unchecked(0).variant() + ), + } + } + } + } + muted + } +}