145
crates/notedeck/src/account/contacts.rs
Normal file
145
crates/notedeck/src/account/contacts.rs
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
|
use enostr::Pubkey;
|
||||||
|
use nostrdb::{Filter, Ndb, Note, NoteKey, Subscription, Transaction};
|
||||||
|
|
||||||
|
pub struct Contacts {
|
||||||
|
pub filter: Filter,
|
||||||
|
pub(super) state: ContactState,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ContactState {
|
||||||
|
Unreceived,
|
||||||
|
Received {
|
||||||
|
contacts: HashSet<Pubkey>,
|
||||||
|
note_key: NoteKey,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Debug, Clone, Copy)]
|
||||||
|
pub enum IsFollowing {
|
||||||
|
/// We don't have the contact list, so we don't know
|
||||||
|
Unknown,
|
||||||
|
|
||||||
|
/// We are follow
|
||||||
|
Yes,
|
||||||
|
|
||||||
|
No,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Contacts {
|
||||||
|
pub fn new(pubkey: &[u8; 32]) -> Self {
|
||||||
|
let filter = Filter::new().authors([pubkey]).kinds([3]).limit(1).build();
|
||||||
|
|
||||||
|
Self {
|
||||||
|
filter,
|
||||||
|
state: ContactState::Unreceived,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn query(&mut self, ndb: &Ndb, txn: &Transaction) {
|
||||||
|
let binding = ndb
|
||||||
|
.query(txn, &[self.filter.clone()], 1)
|
||||||
|
.expect("query user relays results");
|
||||||
|
|
||||||
|
let Some(res) = binding.first() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
update_state(&mut self.state, &res.note, res.note_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_following(&self, other: &Pubkey) -> IsFollowing {
|
||||||
|
match &self.state {
|
||||||
|
ContactState::Unreceived => IsFollowing::Unknown,
|
||||||
|
ContactState::Received {
|
||||||
|
contacts,
|
||||||
|
note_key: _,
|
||||||
|
} => {
|
||||||
|
if contacts.contains(other) {
|
||||||
|
IsFollowing::Yes
|
||||||
|
} else {
|
||||||
|
IsFollowing::No
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn poll_for_updates(&mut self, ndb: &Ndb, txn: &Transaction, sub: Subscription) {
|
||||||
|
let nks = ndb.poll_for_notes(sub, 1);
|
||||||
|
|
||||||
|
let Some(key) = nks.first() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let note = match ndb.get_note_by_key(txn, *key) {
|
||||||
|
Ok(note) => note,
|
||||||
|
Err(e) => {
|
||||||
|
tracing::error!("Could not find note at key {:?}: {e}", key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
update_state(&mut self.state, ¬e, *key);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_state(&self) -> &ContactState {
|
||||||
|
&self.state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_state(state: &mut ContactState, note: &Note, key: NoteKey) {
|
||||||
|
match state {
|
||||||
|
ContactState::Unreceived => {
|
||||||
|
*state = ContactState::Received {
|
||||||
|
contacts: get_contacts_owned(note),
|
||||||
|
note_key: key,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ContactState::Received { contacts, note_key } => {
|
||||||
|
update_contacts(contacts, note);
|
||||||
|
*note_key = key;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_contacts<'a>(note: &Note<'a>) -> HashSet<&'a [u8; 32]> {
|
||||||
|
let mut contacts = HashSet::with_capacity(note.tags().count().into());
|
||||||
|
|
||||||
|
for tag in note.tags() {
|
||||||
|
if tag.count() < 2 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some("p") = tag.get_str(0) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
let Some(cur_id) = tag.get_id(1) else {
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
contacts.insert(cur_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
contacts
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_contacts_owned(note: &Note<'_>) -> HashSet<Pubkey> {
|
||||||
|
get_contacts(note)
|
||||||
|
.iter()
|
||||||
|
.map(|p| Pubkey::new(**p))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_contacts(cur: &mut HashSet<Pubkey>, new: &Note<'_>) {
|
||||||
|
let new_contacts = get_contacts(new);
|
||||||
|
|
||||||
|
cur.retain(|pk| new_contacts.contains(pk.bytes()));
|
||||||
|
|
||||||
|
new_contacts.iter().for_each(|c| {
|
||||||
|
if !cur.contains(*c) {
|
||||||
|
cur.insert(Pubkey::new(**c));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
pub mod accounts;
|
pub mod accounts;
|
||||||
pub mod cache;
|
pub mod cache;
|
||||||
|
pub mod contacts;
|
||||||
pub mod mute;
|
pub mod mute;
|
||||||
pub mod relay;
|
pub mod relay;
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ mod wallet;
|
|||||||
mod zaps;
|
mod zaps;
|
||||||
|
|
||||||
pub use account::accounts::{AccountData, Accounts};
|
pub use account::accounts::{AccountData, Accounts};
|
||||||
|
pub use account::contacts::{ContactState, IsFollowing};
|
||||||
pub use account::relay::RelayAction;
|
pub use account::relay::RelayAction;
|
||||||
pub use account::FALLBACK_PUBKEY;
|
pub use account::FALLBACK_PUBKEY;
|
||||||
pub use app::{App, AppAction, Notedeck};
|
pub use app::{App, AppAction, Notedeck};
|
||||||
|
|||||||
Reference in New Issue
Block a user