wip: refactor

This commit is contained in:
reya
2025-01-11 09:16:30 +07:00
parent 38d0061796
commit 08980e55a4
10 changed files with 379 additions and 428 deletions
-199
View File
@@ -1,199 +0,0 @@
use crate::{get_client, utils::room_hash};
use gpui::{AppContext, Context, Global, Model, SharedString, WeakModel};
use itertools::Itertools;
use nostr_sdk::prelude::*;
use profile::cut_public_key;
use rnglib::{Language, RNG};
use serde::Deserialize;
use std::{
cmp::Reverse,
collections::HashMap,
sync::{Arc, RwLock},
};
#[derive(Clone, PartialEq, Eq, Deserialize)]
pub struct Member {
public_key: PublicKey,
metadata: Metadata,
}
impl Member {
pub fn new(public_key: PublicKey, metadata: Metadata) -> Self {
Self {
public_key,
metadata,
}
}
pub fn public_key(&self) -> PublicKey {
self.public_key
}
pub fn metadata(&self) -> Metadata {
self.metadata.clone()
}
pub fn name(&self) -> String {
if let Some(display_name) = &self.metadata.display_name {
if !display_name.is_empty() {
return display_name.clone();
}
}
if let Some(name) = &self.metadata.name {
if !name.is_empty() {
return name.clone();
}
}
cut_public_key(self.public_key)
}
}
#[derive(Clone, PartialEq, Eq, Deserialize)]
pub struct Room {
pub id: SharedString,
pub owner: PublicKey,
pub members: Vec<Member>,
pub last_seen: Timestamp,
pub title: Option<SharedString>,
}
impl Room {
pub fn new(
id: SharedString,
owner: PublicKey,
last_seen: Timestamp,
title: Option<SharedString>,
members: Vec<Member>,
) -> Self {
let title = if title.is_none() {
let rng = RNG::from(&Language::Roman);
let name = rng.generate_names(2, true).join("-").to_lowercase();
Some(name.into())
} else {
title
};
Self {
id,
title,
members,
last_seen,
owner,
}
}
}
#[derive(Clone, Debug)]
pub struct Message {
pub event: Event,
pub metadata: Option<Metadata>,
}
impl Message {
pub fn new(event: Event, metadata: Option<Metadata>) -> Self {
// TODO: parse event's content
Self { event, metadata }
}
}
type Inbox = Vec<Event>;
type Messages = RwLock<HashMap<SharedString, Arc<RwLock<Vec<Message>>>>>;
pub struct ChatRegistry {
messages: Model<Messages>,
inbox: Model<Inbox>,
}
impl Global for ChatRegistry {}
impl ChatRegistry {
pub fn set_global(cx: &mut AppContext) {
let inbox = cx.new_model(|_| Vec::new());
let messages = cx.new_model(|_| RwLock::new(HashMap::new()));
cx.set_global(Self { inbox, messages });
}
pub fn init(&mut self, cx: &mut AppContext) {
let async_cx = cx.to_async();
// Get all current room's hashes
let hashes: Vec<u64> = self
.inbox
.read(cx)
.iter()
.map(|ev| room_hash(&ev.tags))
.collect();
cx.foreground_executor()
.spawn(async move {
let client = get_client();
let query: anyhow::Result<Vec<Event>, anyhow::Error> = async_cx
.background_executor()
.spawn(async move {
let signer = client.signer().await?;
let public_key = signer.get_public_key().await?;
let filter = Filter::new()
.kind(Kind::PrivateDirectMessage)
.author(public_key);
// Get all DM events from database
let events = client.database().query(vec![filter]).await?;
// Filter result
// 1. Only new rooms
// 2. Only unique rooms
// 3. Sorted by created_at
let result = events
.into_iter()
.filter(|ev| !hashes.iter().any(|h| h == &room_hash(&ev.tags)))
.unique_by(|ev| room_hash(&ev.tags))
.sorted_by_key(|ev| Reverse(ev.created_at))
.collect::<Vec<_>>();
Ok(result)
})
.await;
if let Ok(events) = query {
_ = async_cx.update_global::<Self, _>(|state, cx| {
state.inbox.update(cx, |model, cx| {
model.extend(events);
cx.notify();
});
});
}
})
.detach();
}
pub fn new_message(&mut self, event: Event, metadata: Option<Metadata>, cx: &mut AppContext) {
// Get room id
let room_id = SharedString::from(room_hash(&event.tags).to_string());
// Create message
let message = Message::new(event, metadata);
self.messages.update(cx, |this, cx| {
this.write()
.unwrap()
.entry(room_id)
.or_insert(Arc::new(RwLock::new(Vec::new())))
.write()
.unwrap()
.push(message);
cx.notify();
});
}
pub fn messages(&self) -> WeakModel<Messages> {
self.messages.downgrade()
}
pub fn inbox(&self) -> WeakModel<Inbox> {
self.inbox.downgrade()
}
}
+173
View File
@@ -0,0 +1,173 @@
use crate::{get_client, utils::room_hash};
use gpui::{AppContext, Context, Global, Model, SharedString, WeakModel};
use itertools::Itertools;
use nostr_sdk::prelude::*;
use room::Room;
use std::{
cmp::Reverse,
collections::HashMap,
sync::{Arc, RwLock},
};
pub mod room;
#[derive(Clone, Debug)]
pub struct NewMessage {
pub event: Event,
pub metadata: Metadata,
}
impl NewMessage {
pub fn new(event: Event, metadata: Metadata) -> Self {
// TODO: parse event's content
Self { event, metadata }
}
}
type NewMessages = RwLock<HashMap<SharedString, Arc<RwLock<Vec<NewMessage>>>>>;
pub struct ChatRegistry {
inbox: Model<Vec<Model<Room>>>,
new_messages: Model<NewMessages>,
}
impl Global for ChatRegistry {}
impl ChatRegistry {
pub fn set_global(cx: &mut AppContext) {
let inbox = cx.new_model(|_| Vec::new());
let new_messages = cx.new_model(|_| RwLock::new(HashMap::new()));
cx.observe_new_models::<Room>(|this, cx| {
// Get all pubkeys to load metadata
let pubkeys: Vec<PublicKey> = this.members.iter().map(|m| m.public_key()).collect();
cx.spawn(|weak_model, mut async_cx| async move {
let query: Result<Vec<(PublicKey, Metadata)>, Error> = async_cx
.background_executor()
.spawn(async move {
let client = get_client();
let mut profiles = Vec::new();
for public_key in pubkeys.into_iter() {
let query = client.database().metadata(public_key).await?;
let metadata = query.unwrap_or_default();
profiles.push((public_key, metadata));
}
Ok(profiles)
})
.await;
if let Ok(profiles) = query {
if let Some(model) = weak_model.upgrade() {
_ = async_cx.update_model(&model, |model, cx| {
for profile in profiles.into_iter() {
model.set_metadata(profile.0, profile.1);
}
cx.notify();
});
}
}
})
.detach();
})
.detach();
cx.set_global(Self {
inbox,
new_messages,
});
}
pub fn init(&mut self, cx: &mut AppContext) {
let mut async_cx = cx.to_async();
let async_inbox = self.inbox.clone();
// Get all current room's id
let hashes: Vec<u64> = self
.inbox
.read(cx)
.iter()
.map(|room| room.read(cx).id)
.collect();
cx.foreground_executor()
.spawn(async move {
let client = get_client();
let query: anyhow::Result<Vec<Event>, anyhow::Error> = async_cx
.background_executor()
.spawn(async move {
let signer = client.signer().await?;
let public_key = signer.get_public_key().await?;
let filter = Filter::new()
.kind(Kind::PrivateDirectMessage)
.author(public_key);
// Get all DM events from database
let events = client.database().query(vec![filter]).await?;
// Filter result
// - Only unique rooms
// - Sorted by created_at
let result = events
.into_iter()
.filter(|ev| ev.tags.public_keys().peekable().peek().is_some())
.unique_by(|ev| room_hash(&ev.tags))
.sorted_by_key(|ev| Reverse(ev.created_at))
.collect::<Vec<_>>();
Ok(result)
})
.await;
if let Ok(events) = query {
_ = async_cx.update_model(&async_inbox, |model, cx| {
let items: Vec<Model<Room>> = events
.into_iter()
.filter_map(|ev| {
let id = room_hash(&ev.tags);
// Filter all seen events
if !hashes.iter().any(|h| h == &id) {
Some(cx.new_model(|_| Room::new(&ev)))
} else {
None
}
})
.collect();
model.extend(items);
cx.notify();
});
}
})
.detach();
}
pub fn inbox(&self) -> WeakModel<Vec<Model<Room>>> {
self.inbox.downgrade()
}
pub fn new_messages(&self) -> WeakModel<NewMessages> {
self.new_messages.downgrade()
}
pub fn receive(&mut self, event: Event, metadata: Metadata, cx: &mut AppContext) {
let entry = room_hash(&event.tags).to_string().into();
let message = NewMessage::new(event, metadata);
self.new_messages.update(cx, |this, cx| {
this.write()
.unwrap()
.entry(entry)
.or_insert(Arc::new(RwLock::new(Vec::new())))
.write()
.unwrap()
.push(message);
cx.notify();
})
}
}
+97
View File
@@ -0,0 +1,97 @@
use crate::utils::{room_hash, shorted_public_key};
use gpui::SharedString;
use nostr_sdk::prelude::*;
use rnglib::{Language, RNG};
#[derive(Debug, Clone)]
pub struct Member {
public_key: PublicKey,
metadata: Metadata,
}
impl Member {
pub fn new(public_key: PublicKey, metadata: Metadata) -> Self {
Self {
public_key,
metadata,
}
}
pub fn public_key(&self) -> PublicKey {
self.public_key
}
pub fn metadata(&self) -> Metadata {
self.metadata.clone()
}
pub fn name(&self) -> String {
if let Some(display_name) = &self.metadata.display_name {
if !display_name.is_empty() {
return display_name.clone();
}
}
if let Some(name) = &self.metadata.name {
if !name.is_empty() {
return name.clone();
}
}
shorted_public_key(self.public_key)
}
pub fn update(&mut self, metadata: &Metadata) {
self.metadata = metadata.clone()
}
}
#[derive(Debug)]
pub struct Room {
pub id: u64,
pub title: Option<SharedString>,
pub members: Vec<Member>,
pub last_seen: Timestamp,
pub is_group: bool,
}
impl Room {
pub fn new(event: &Event) -> Self {
let id = room_hash(&event.tags);
let last_seen = event.created_at;
let members: Vec<Member> = event
.tags
.public_keys()
.copied()
.map(|public_key| Member::new(public_key, Metadata::default()))
.collect();
let title = if let Some(tag) = event.tags.find(TagKind::Title) {
tag.content().map(|s| s.to_owned().into())
} else {
let rng = RNG::from(&Language::Roman);
let name = rng.generate_names(2, true).join("-").to_lowercase();
Some(name.into())
};
let is_group = members.len() > 1;
Self {
id,
members,
title,
last_seen,
is_group,
}
}
pub fn set_metadata(&mut self, public_key: PublicKey, metadata: Metadata) {
for member in self.members.iter_mut() {
if member.public_key() == public_key {
member.update(&metadata);
}
}
}
}