.
This commit is contained in:
@@ -7,9 +7,12 @@ use anyhow::{anyhow, Error};
|
||||
use common::{EventUtils, BOOTSTRAP_RELAYS};
|
||||
use gpui::{App, AppContext, Context, Entity, Global, Task};
|
||||
use nostr_sdk::prelude::*;
|
||||
pub use person::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use state::{NostrRegistry, TIMEOUT};
|
||||
|
||||
mod person;
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
PersonRegistry::set_global(cx.new(PersonRegistry::new), cx);
|
||||
}
|
||||
@@ -22,7 +25,7 @@ impl Global for GlobalPersonRegistry {}
|
||||
#[derive(Debug)]
|
||||
pub struct PersonRegistry {
|
||||
/// Collection of all persons (user profiles)
|
||||
persons: HashMap<PublicKey, Entity<Profile>>,
|
||||
persons: HashMap<PublicKey, Entity<Person>>,
|
||||
|
||||
/// Set of public keys that have been seen
|
||||
seen: Rc<RefCell<HashSet<PublicKey>>>,
|
||||
@@ -51,7 +54,7 @@ impl PersonRegistry {
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
// Channel for communication between nostr and gpui
|
||||
let (tx, rx) = flume::bounded::<Profile>(100);
|
||||
let (tx, rx) = flume::bounded::<Person>(100);
|
||||
let (mta_tx, mta_rx) = flume::bounded::<PublicKey>(100);
|
||||
|
||||
let mut tasks = smallvec![];
|
||||
@@ -81,9 +84,9 @@ impl PersonRegistry {
|
||||
tasks.push(
|
||||
// Update GPUI state
|
||||
cx.spawn(async move |this, cx| {
|
||||
while let Ok(profile) = rx.recv_async().await {
|
||||
while let Ok(person) = rx.recv_async().await {
|
||||
this.update(cx, |this, cx| {
|
||||
this.insert(profile, cx);
|
||||
this.insert(person, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
@@ -99,9 +102,9 @@ impl PersonRegistry {
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(profiles) => {
|
||||
Ok(persons) => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.bulk_inserts(profiles, cx);
|
||||
this.bulk_inserts(persons, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
@@ -121,7 +124,7 @@ impl PersonRegistry {
|
||||
}
|
||||
|
||||
/// Handle nostr notifications
|
||||
async fn handle_notifications(client: &Client, tx: &flume::Sender<Profile>) {
|
||||
async fn handle_notifications(client: &Client, tx: &flume::Sender<Person>) {
|
||||
let mut notifications = client.notifications();
|
||||
let mut processed_events = HashSet::new();
|
||||
|
||||
@@ -140,9 +143,9 @@ impl PersonRegistry {
|
||||
match event.kind {
|
||||
Kind::Metadata => {
|
||||
let metadata = Metadata::from_json(&event.content).unwrap_or_default();
|
||||
let profile = Profile::new(event.pubkey, metadata);
|
||||
let person = Person::new(event.pubkey, metadata);
|
||||
|
||||
tx.send_async(profile).await.ok();
|
||||
tx.send_async(person).await.ok();
|
||||
}
|
||||
Kind::ContactList => {
|
||||
let public_keys = event.extract_public_keys();
|
||||
@@ -214,51 +217,50 @@ impl PersonRegistry {
|
||||
}
|
||||
|
||||
/// Load all user profiles from the database
|
||||
async fn load_persons(client: &Client) -> Result<Vec<Profile>, Error> {
|
||||
async fn load_persons(client: &Client) -> Result<Vec<Person>, Error> {
|
||||
let filter = Filter::new().kind(Kind::Metadata).limit(200);
|
||||
let events = client.database().query(filter).await?;
|
||||
|
||||
let mut profiles = vec![];
|
||||
let mut persons = 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);
|
||||
let person = Person::new(event.pubkey, metadata);
|
||||
persons.push(person);
|
||||
}
|
||||
|
||||
Ok(profiles)
|
||||
Ok(persons)
|
||||
}
|
||||
|
||||
/// Insert batch of persons
|
||||
fn bulk_inserts(&mut self, profiles: Vec<Profile>, cx: &mut Context<Self>) {
|
||||
for profile in profiles.into_iter() {
|
||||
self.persons
|
||||
.insert(profile.public_key(), cx.new(|_| profile));
|
||||
fn bulk_inserts(&mut self, persons: Vec<Person>, cx: &mut Context<Self>) {
|
||||
for person in persons.into_iter() {
|
||||
self.persons.insert(person.public_key(), cx.new(|_| person));
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
/// Insert or update a person
|
||||
pub fn insert(&mut self, profile: Profile, cx: &mut App) {
|
||||
let public_key = profile.public_key();
|
||||
pub fn insert(&mut self, person: Person, cx: &mut App) {
|
||||
let public_key = person.public_key();
|
||||
|
||||
match self.persons.get(&public_key) {
|
||||
Some(person) => {
|
||||
person.update(cx, |this, cx| {
|
||||
*this = profile;
|
||||
Some(this) => {
|
||||
this.update(cx, |this, cx| {
|
||||
*this = person;
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
None => {
|
||||
self.persons.insert(public_key, cx.new(|_| profile));
|
||||
self.persons.insert(public_key, cx.new(|_| person));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get single person by public key
|
||||
pub fn get(&self, public_key: &PublicKey, cx: &App) -> Profile {
|
||||
if let Some(profile) = self.persons.get(public_key) {
|
||||
return profile.read(cx).clone();
|
||||
pub fn get(&self, public_key: &PublicKey, cx: &App) -> Person {
|
||||
if let Some(person) = self.persons.get(public_key) {
|
||||
return person.read(cx).clone();
|
||||
}
|
||||
|
||||
let public_key = *public_key;
|
||||
@@ -277,6 +279,6 @@ impl PersonRegistry {
|
||||
}
|
||||
|
||||
// Return a temporary profile with default metadata
|
||||
Profile::new(public_key, Metadata::default())
|
||||
Person::new(public_key, Metadata::default())
|
||||
}
|
||||
}
|
||||
|
||||
111
crates/person/src/person.rs
Normal file
111
crates/person/src/person.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
use gpui::SharedString;
|
||||
use nostr_sdk::prelude::*;
|
||||
use state::Announcement;
|
||||
|
||||
/// Person
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Person {
|
||||
public_key: PublicKey,
|
||||
metadata: Metadata,
|
||||
announcement: Option<Announcement>,
|
||||
}
|
||||
|
||||
impl PartialEq for Person {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.public_key == other.public_key
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Person {}
|
||||
|
||||
impl PartialOrd for Person {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Person {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.name().cmp(&other.name())
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for Person {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.public_key.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PublicKey> for Person {
|
||||
fn from(public_key: PublicKey) -> Self {
|
||||
Self::new(public_key, Metadata::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl Person {
|
||||
pub fn new(public_key: PublicKey, metadata: Metadata) -> Self {
|
||||
Self {
|
||||
public_key,
|
||||
metadata,
|
||||
announcement: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get profile public key
|
||||
pub fn public_key(&self) -> PublicKey {
|
||||
self.public_key
|
||||
}
|
||||
|
||||
/// Get profile metadata
|
||||
pub fn metadata(&self) -> Metadata {
|
||||
self.metadata.clone()
|
||||
}
|
||||
|
||||
/// Get profile encryption keys announcement
|
||||
pub fn announcement(&self) -> Option<Announcement> {
|
||||
self.announcement.clone()
|
||||
}
|
||||
|
||||
/// Get profile avatar
|
||||
pub fn avatar(&self) -> SharedString {
|
||||
self.metadata()
|
||||
.picture
|
||||
.as_ref()
|
||||
.filter(|picture| !picture.is_empty())
|
||||
.map(|picture| picture.into())
|
||||
.unwrap_or_else(|| "brand/avatar.png".into())
|
||||
}
|
||||
|
||||
/// Get profile name
|
||||
pub fn name(&self) -> SharedString {
|
||||
if let Some(display_name) = self.metadata().display_name.as_ref() {
|
||||
if !display_name.is_empty() {
|
||||
return SharedString::from(display_name);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(name) = self.metadata().name.as_ref() {
|
||||
if !name.is_empty() {
|
||||
return SharedString::from(name);
|
||||
}
|
||||
}
|
||||
|
||||
SharedString::from(shorten_pubkey(self.public_key(), 4))
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorten a [`PublicKey`] to a string with the first and last `len` characters
|
||||
///
|
||||
/// Ex. `00000000:00000002`
|
||||
pub fn shorten_pubkey(public_key: PublicKey, len: usize) -> String {
|
||||
let Ok(pubkey) = public_key.to_bech32();
|
||||
|
||||
format!(
|
||||
"{}:{}",
|
||||
&pubkey[0..(len + 1)],
|
||||
&pubkey[pubkey.len() - len..]
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user