Files
coop/crates/person/src/lib.rs
T
reyaandGitHub 122299f548 chore: improve nip4e implementation (#204)
* patch

* update ui

* add load response

* fix

* .

* wip: rewrite gossip

* new gossip implementation

* clean up

* .

* debug

* .

* .

* update

* .

* fix

* fix
2025-11-15 08:30:45 +07:00

172 lines
5.5 KiB
Rust

use std::collections::{HashMap, HashSet};
use gpui::{App, AppContext, Context, Entity, Global, Task};
use nostr_sdk::prelude::*;
use smallvec::{smallvec, SmallVec};
use state::NostrRegistry;
pub fn init(cx: &mut App) {
PersonRegistry::set_global(cx.new(PersonRegistry::new), cx);
}
struct GlobalPersonRegistry(Entity<PersonRegistry>);
impl Global for GlobalPersonRegistry {}
/// Person Registry
#[derive(Debug)]
pub struct PersonRegistry {
/// Collection of all persons (user profiles)
pub persons: HashMap<PublicKey, Entity<Profile>>,
/// Tasks for asynchronous operations
_tasks: SmallVec<[Task<()>; 2]>,
}
impl PersonRegistry {
/// Retrieve the global person registry state
pub fn global(cx: &App) -> Entity<Self> {
cx.global::<GlobalPersonRegistry>().0.clone()
}
/// Set the global person registry instance
pub(crate) fn set_global(state: Entity<Self>, cx: &mut App) {
cx.set_global(GlobalPersonRegistry(state));
}
/// Create a new person registry instance
pub(crate) fn new(cx: &mut Context<Self>) -> Self {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let mut tasks = smallvec![];
tasks.push(
// Handle notifications
cx.spawn({
let client = nostr.read(cx).client();
async move |this, cx| {
let mut notifications = client.notifications();
let mut processed_events = HashSet::new();
while let Ok(notification) = notifications.recv().await {
let RelayPoolNotification::Message { message, .. } = notification else {
// Skip if the notification is not a message
continue;
};
if let RelayMessage::Event { event, .. } = message {
if !processed_events.insert(event.id) {
// Skip if the event has already been processed
continue;
}
if event.kind != Kind::Metadata {
// Skip if the event is not a metadata event
continue;
};
let metadata = Metadata::from_json(&event.content).unwrap_or_default();
let profile = Profile::new(event.pubkey, metadata);
this.update(cx, |this, cx| {
this.insert_or_update_person(profile, cx);
})
.expect("Entity has been released")
}
}
}
}),
);
tasks.push(
// Load all user profiles from the database
cx.spawn(async move |this, cx| {
let result = cx
.background_spawn(async move { Self::load_persons(&client).await })
.await;
match result {
Ok(profiles) => {
this.update(cx, |this, cx| {
this.bulk_insert_persons(profiles, cx);
})
.ok();
}
Err(e) => {
log::error!("Failed to load persons: {e}");
}
};
}),
);
Self {
persons: HashMap::new(),
_tasks: tasks,
}
}
/// Load all user profiles from the database
async fn load_persons(client: &Client) -> Result<Vec<Profile>, Error> {
let filter = Filter::new().kind(Kind::Metadata).limit(200);
let events = client.database().query(filter).await?;
let mut profiles = vec![];
for event in events.into_iter() {
let metadata = Metadata::from_json(event.content).unwrap_or_default();
let profile = Profile::new(event.pubkey, metadata);
profiles.push(profile);
}
Ok(profiles)
}
/// Insert batch of persons
fn bulk_insert_persons(&mut self, profiles: Vec<Profile>, cx: &mut Context<Self>) {
for profile in profiles.into_iter() {
self.persons
.insert(profile.public_key(), cx.new(|_| profile));
}
cx.notify();
}
/// Insert or update a person
pub fn insert_or_update_person(&mut self, profile: Profile, cx: &mut App) {
let public_key = profile.public_key();
match self.persons.get(&public_key) {
Some(person) => {
person.update(cx, |this, cx| {
*this = profile;
cx.notify();
});
}
None => {
self.persons.insert(public_key, cx.new(|_| profile));
}
}
}
/// Get single person
pub fn get_person(&self, public_key: &PublicKey, cx: &App) -> Profile {
self.persons
.get(public_key)
.map(|e| e.read(cx))
.cloned()
.unwrap_or(Profile::new(public_key.to_owned(), Metadata::default()))
}
/// Get group of persons
pub fn get_group_person(&self, public_keys: &[PublicKey], cx: &App) -> Vec<Profile> {
let mut profiles = vec![];
for public_key in public_keys.iter() {
let profile = self.get_person(public_key, cx);
profiles.push(profile);
}
profiles
}
}