This commit is contained in:
2026-01-05 10:51:25 +07:00
parent 23f8cc49c6
commit e1ed8483ba
10 changed files with 96 additions and 722 deletions

View File

@@ -11,9 +11,7 @@ use flume::Sender;
use fuzzy_matcher::skim::SkimMatcherV2;
use fuzzy_matcher::FuzzyMatcher;
use gpui::{App, AppContext, Context, Entity, EventEmitter, Global, Task};
pub use message::*;
use nostr_sdk::prelude::*;
pub use room::*;
use settings::AppSettings;
use smallvec::{smallvec, SmallVec};
use state::{tracker, NostrRegistry, GIFTWRAP_SUBSCRIPTION};
@@ -21,6 +19,9 @@ use state::{tracker, NostrRegistry, GIFTWRAP_SUBSCRIPTION};
mod message;
mod room;
pub use message::*;
pub use room::*;
pub fn init(cx: &mut App) {
ChatRegistry::set_global(cx.new(ChatRegistry::new), cx);
}
@@ -42,17 +43,22 @@ pub struct ChatRegistry {
_tasks: SmallVec<[Task<()>; 4]>,
}
/// Chat event.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum ChatEvent {
OpenRoom(u64),
CloseRoom(u64),
NewChatRequest(RoomKind),
NewRequest(RoomKind),
}
/// Channel signal.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Signal {
Loading(bool),
enum Signal {
/// Message received from relay pool
Message(NewMessage),
/// Loading status of the registry
Loading(bool),
/// Eose received from relay pool
Eose,
}
@@ -507,7 +513,6 @@ impl ChatRegistry {
if let Some(room) = self.rooms.iter().find(|room| room.read(cx).id == id) {
let is_new_event = message.rumor.created_at > room.read(cx).created_at;
let created_at = message.rumor.created_at;
let event_for_emit = message.rumor.clone();
// Update room
room.update(cx, |this, cx| {
@@ -521,7 +526,7 @@ impl ChatRegistry {
}
// Emit the new message to the room
this.emit_message(message.gift_wrap, event_for_emit.clone(), cx);
this.emit_message(message, cx);
});
// Resort all rooms in the registry by their created at (after updated)
@@ -533,7 +538,7 @@ impl ChatRegistry {
self.add_room(cx.new(|_| Room::from(&message.rumor)), cx);
// Notify the UI about the new room
cx.emit(ChatEvent::NewChatRequest(RoomKind::default()));
cx.emit(ChatEvent::NewRequest(RoomKind::default()));
}
}

View File

@@ -2,6 +2,7 @@ use std::hash::Hash;
use nostr_sdk::prelude::*;
/// New message.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct NewMessage {
pub gift_wrap: EventId,
@@ -14,6 +15,7 @@ impl NewMessage {
}
}
/// Message.
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub enum Message {
User(RenderedMessage),
@@ -22,11 +24,17 @@ pub enum Message {
}
impl Message {
pub fn user(user: impl Into<RenderedMessage>) -> Self {
pub fn user<I>(user: I) -> Self
where
I: Into<RenderedMessage>,
{
Self::User(user.into())
}
pub fn warning(content: impl Into<String>) -> Self {
pub fn warning<I>(content: I) -> Self
where
I: Into<String>,
{
Self::Warning(content.into(), Timestamp::now())
}
@@ -43,6 +51,18 @@ impl Message {
}
}
impl From<&NewMessage> for Message {
fn from(val: &NewMessage) -> Self {
Self::User(val.into())
}
}
impl From<&UnsignedEvent> for Message {
fn from(val: &UnsignedEvent) -> Self {
Self::User(val.into())
}
}
impl Ord for Message {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
match (self, other) {
@@ -63,6 +83,7 @@ impl PartialOrd for Message {
}
}
/// Rendered message.
#[derive(Debug, Clone)]
pub struct RenderedMessage {
pub id: EventId,
@@ -78,48 +99,53 @@ pub struct RenderedMessage {
pub replies_to: Vec<EventId>,
}
impl From<Event> for RenderedMessage {
fn from(inner: Event) -> Self {
let mentions = extract_mentions(&inner.content);
let replies_to = extract_reply_ids(&inner.tags);
impl From<&Event> for RenderedMessage {
fn from(val: &Event) -> Self {
let mentions = extract_mentions(&val.content);
let replies_to = extract_reply_ids(&val.tags);
Self {
id: inner.id,
author: inner.pubkey,
content: inner.content,
created_at: inner.created_at,
id: val.id,
author: val.pubkey,
content: val.content.clone(),
created_at: val.created_at,
mentions,
replies_to,
}
}
}
impl From<UnsignedEvent> for RenderedMessage {
fn from(inner: UnsignedEvent) -> Self {
let mentions = extract_mentions(&inner.content);
let replies_to = extract_reply_ids(&inner.tags);
impl From<&UnsignedEvent> for RenderedMessage {
fn from(val: &UnsignedEvent) -> Self {
let mentions = extract_mentions(&val.content);
let replies_to = extract_reply_ids(&val.tags);
Self {
// Event ID must be known
id: inner.id.unwrap(),
author: inner.pubkey,
content: inner.content,
created_at: inner.created_at,
id: val.id.unwrap(),
author: val.pubkey,
content: val.content.clone(),
created_at: val.created_at,
mentions,
replies_to,
}
}
}
impl From<Box<Event>> for RenderedMessage {
fn from(inner: Box<Event>) -> Self {
(*inner).into()
}
}
impl From<&NewMessage> for RenderedMessage {
fn from(val: &NewMessage) -> Self {
let mentions = extract_mentions(&val.rumor.content);
let replies_to = extract_reply_ids(&val.rumor.tags);
impl From<&Box<Event>> for RenderedMessage {
fn from(inner: &Box<Event>) -> Self {
inner.to_owned().into()
Self {
// Event ID must be known
id: val.rumor.id.unwrap(),
author: val.rumor.pubkey,
content: val.rumor.content.clone(),
created_at: val.rumor.created_at,
mentions,
replies_to,
}
}
}
@@ -149,6 +175,7 @@ impl Hash for RenderedMessage {
}
}
/// Extracts all mentions (public keys) from a content string.
fn extract_mentions(content: &str) -> Vec<PublicKey> {
let parser = NostrParser::new();
let tokens = parser.parse(content);
@@ -165,6 +192,7 @@ fn extract_mentions(content: &str) -> Vec<PublicKey> {
.collect::<Vec<_>>()
}
/// Extracts all reply (ids) from the event tags.
fn extract_reply_ids(inner: &Tags) -> Vec<EventId> {
let mut replies_to = vec![];

View File

@@ -11,6 +11,8 @@ use nostr_sdk::prelude::*;
use person::PersonRegistry;
use state::{tracker, NostrRegistry};
use crate::NewMessage;
const SEND_RETRY: usize = 10;
#[derive(Debug, Clone)]
@@ -80,17 +82,21 @@ impl SendReport {
}
}
#[derive(Debug, Clone)]
pub enum RoomSignal {
NewMessage((EventId, UnsignedEvent)),
Refresh,
/// Room event.
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum RoomEvent {
/// Incoming message.
Incoming(NewMessage),
/// Reloads the current room's messages.
Reload,
}
/// Room kind.
#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
pub enum RoomKind {
Ongoing,
#[default]
Request,
Ongoing,
}
#[derive(Debug)]
@@ -133,7 +139,7 @@ impl Hash for Room {
impl Eq for Room {}
impl EventEmitter<RoomSignal> for Room {}
impl EventEmitter<RoomEvent> for Room {}
impl From<&UnsignedEvent> for Room {
fn from(val: &UnsignedEvent) -> Self {
@@ -304,13 +310,13 @@ impl Room {
}
/// Emits a new message signal to the current room
pub fn emit_message(&self, id: EventId, event: UnsignedEvent, cx: &mut Context<Self>) {
cx.emit(RoomSignal::NewMessage((id, event)));
pub fn emit_message(&self, message: NewMessage, cx: &mut Context<Self>) {
cx.emit(RoomEvent::Incoming(message));
}
/// Emits a signal to refresh the current room's messages.
/// Emits a signal to reload the current room's messages.
pub fn emit_refresh(&mut self, cx: &mut Context<Self>) {
cx.emit(RoomSignal::Refresh);
cx.emit(RoomEvent::Reload);
}
/// Get gossip relays for each member