This commit is contained in:
2026-06-05 13:31:30 +07:00
parent d53e9d538c
commit c791309659
4 changed files with 71 additions and 69 deletions

View File

@@ -16,7 +16,7 @@ use gpui::{
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use smallvec::{SmallVec, smallvec}; use smallvec::{SmallVec, smallvec};
use smol::lock::RwLock; use smol::lock::RwLock;
use state::{CoopSigner, DEVICE_GIFTWRAP, NostrRegistry, StateEvent, TIMEOUT, USER_GIFTWRAP}; use state::{CoopSigner, DEVICE_GIFTWRAP, NostrRegistry, StateEvent, USER_GIFTWRAP};
mod message; mod message;
mod room; mod room;
@@ -41,6 +41,8 @@ pub enum ChatEvent {
CloseRoom(u64), CloseRoom(u64),
/// An event to notify UI about a new chat request /// An event to notify UI about a new chat request
Ping, Ping,
/// No Inbox Relays found, the app is not ready to subscribe messages
InboxRelayNotFound,
/// An error occurred /// An error occurred
Error(SharedString), Error(SharedString),
} }
@@ -85,9 +87,6 @@ impl Signal {
} }
} }
type Dekey = bool;
type GiftWrapId = EventId;
/// Chat Registry /// Chat Registry
#[derive(Debug)] #[derive(Debug)]
pub struct ChatRegistry { pub struct ChatRegistry {
@@ -101,10 +100,13 @@ pub struct ChatRegistry {
seens: Arc<RwLock<HashMap<EventId, HashSet<RelayUrl>>>>, seens: Arc<RwLock<HashMap<EventId, HashSet<RelayUrl>>>>,
/// Mapping of unwrapped event ids to their gift wrap event ids /// Mapping of unwrapped event ids to their gift wrap event ids
event_map: Arc<RwLock<HashMap<EventId, (GiftWrapId, Dekey)>>>, event_map: Arc<RwLock<HashMap<EventId, EventId>>>,
/// Tracking the status of unwrapping gift wrap events. /// Tracking the status of unwrapping gift wrap events.
tracking_flag: Arc<AtomicBool>, tracking: Arc<AtomicBool>,
/// Whether the messaging relays have been found.
msg_relays_existed: Arc<AtomicBool>,
/// Channel for sending signals to the UI. /// Channel for sending signals to the UI.
signal_tx: flume::Sender<Signal>, signal_tx: flume::Sender<Signal>,
@@ -162,7 +164,8 @@ impl ChatRegistry {
trashes: cx.new(|_| BTreeSet::default()), trashes: cx.new(|_| BTreeSet::default()),
seens: Arc::new(RwLock::new(HashMap::default())), seens: Arc::new(RwLock::new(HashMap::default())),
event_map: Arc::new(RwLock::new(HashMap::default())), event_map: Arc::new(RwLock::new(HashMap::default())),
tracking_flag: Arc::new(AtomicBool::new(false)), tracking: Arc::new(AtomicBool::new(false)),
msg_relays_existed: Arc::new(AtomicBool::new(false)),
signal_rx: rx, signal_rx: rx,
signal_tx: tx, signal_tx: tx,
tasks: smallvec![], tasks: smallvec![],
@@ -175,7 +178,10 @@ impl ChatRegistry {
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client(); let client = nostr.read(cx).client();
let signer = nostr.read(cx).signer(); let signer = nostr.read(cx).signer();
let status = self.tracking_flag.clone();
let tracking = self.tracking.clone();
let msg_relays_existed = self.msg_relays_existed.clone();
let seens = self.seens.clone(); let seens = self.seens.clone();
let event_map = self.event_map.clone(); let event_map = self.event_map.clone();
let trashes = self.trashes.downgrade(); let trashes = self.trashes.downgrade();
@@ -199,10 +205,7 @@ impl ChatRegistry {
}; };
match *message { match *message {
RelayMessage::Event { RelayMessage::Event { event, .. } => {
event,
subscription_id,
} => {
// Keep track of which relays have seen this event // Keep track of which relays have seen this event
{ {
let mut seens = seens.write().await; let mut seens = seens.write().await;
@@ -219,6 +222,9 @@ impl ChatRegistry {
let current_user = signer.get_public_key().await?; let current_user = signer.get_public_key().await?;
if event.pubkey == current_user { if event.pubkey == current_user {
// Mark that the msg relays have been found
msg_relays_existed.store(true, Ordering::Release);
// Emit the inbox ready signal
tx.send_async(Signal::inbox_ready(&event)).await?; tx.send_async(Signal::inbox_ready(&event)).await?;
} }
} }
@@ -234,8 +240,7 @@ impl ChatRegistry {
// Map the rumor id to the gift wrap event id for later lookup // Map the rumor id to the gift wrap event id for later lookup
{ {
let mut event_map = event_map.write().await; let mut event_map = event_map.write().await;
let dekey = subscription_id.as_ref() == &sub_id1; event_map.insert(rumor.id.unwrap(), event.id);
event_map.insert(rumor.id.unwrap(), (event.id, dekey));
} }
// Check if the rumor has a recipient // Check if the rumor has a recipient
@@ -250,7 +255,7 @@ impl ChatRegistry {
tx.send_async(signal).await?; tx.send_async(signal).await?;
} else { } else {
// Mark the chat still processing new messages // Mark the chat still processing new messages
status.store(true, Ordering::Release); tracking.store(true, Ordering::Release);
} }
} }
Err(e) => { Err(e) => {
@@ -287,7 +292,7 @@ impl ChatRegistry {
} }
Signal::InboxRelayNotFound => { Signal::InboxRelayNotFound => {
this.update(cx, |_this, cx| { this.update(cx, |_this, cx| {
cx.emit(ChatEvent::Error("Messaging Relays not found".into())); cx.emit(ChatEvent::InboxRelayNotFound);
})?; })?;
} }
Signal::Eose => { Signal::Eose => {
@@ -310,7 +315,7 @@ impl ChatRegistry {
/// Tracking the status of unwrapping gift wrap events. /// Tracking the status of unwrapping gift wrap events.
fn tracking(&mut self, cx: &mut Context<Self>) { fn tracking(&mut self, cx: &mut Context<Self>) {
let status = self.tracking_flag.clone(); let status = self.tracking.clone();
let tx = self.signal_tx.clone(); let tx = self.signal_tx.clone();
self.tasks.push(cx.background_spawn(async move { self.tasks.push(cx.background_spawn(async move {
@@ -328,8 +333,8 @@ impl ChatRegistry {
})); }));
} }
/// Get contact list from relays /// Get all necessary metadata from relays for current user
fn get_metadata(&mut self, cx: &mut Context<Self>) { pub fn get_metadata(&mut self, cx: &mut Context<Self>) {
let nostr = NostrRegistry::global(cx); let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client(); let client = nostr.read(cx).client();
let signer = nostr.read(cx).signer(); let signer = nostr.read(cx).signer();
@@ -339,9 +344,7 @@ impl ChatRegistry {
}; };
self.tasks.push(cx.background_spawn(async move { self.tasks.push(cx.background_spawn(async move {
let opts = SubscribeAutoCloseOptions::default() let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE);
.exit_policy(ReqExitPolicy::ExitOnEOSE)
.timeout(Some(Duration::from_secs(TIMEOUT)));
// Construct filter for msg relays // Construct filter for msg relays
let msg_relays = Filter::new() let msg_relays = Filter::new()
@@ -358,29 +361,27 @@ impl ChatRegistry {
// Subscribe // Subscribe
client client
.subscribe(vec![msg_relays, contact_list]) .subscribe(vec![msg_relays, contact_list])
.with_id(SubscriptionId::new("user-meta"))
.close_on(opts) .close_on(opts)
.await?; .await?;
Ok(()) Ok(())
})); }));
let client = nostr.read(cx).client();
let tx = self.signal_tx.clone(); let tx = self.signal_tx.clone();
let msg_relays_existed = self.msg_relays_existed.clone();
// Reset the status flag
msg_relays_existed.store(false, Ordering::Release);
// Wait for the msg relays to be found or timeout
self.tasks.push(cx.background_spawn(async move { self.tasks.push(cx.background_spawn(async move {
loop { // Wait for 5 seconds
let filter = Filter::new() smol::Timer::after(Duration::from_secs(5)).await;
.kind(Kind::InboxRelays)
.author(public_key)
.limit(1);
if client.database().query(filter).await?.first().is_some() { // Then check if the msg relays have been found
break; if !msg_relays_existed.load(Ordering::Acquire) {
} else { tx.send_async(Signal::inbox_relay_not_found()).await?;
tx.send_async(Signal::inbox_relay_not_found()).await?;
}
smol::Timer::after(Duration::from_secs(5)).await;
} }
Ok(()) Ok(())
@@ -439,7 +440,7 @@ impl ChatRegistry {
/// Get the loading status of the chat registry /// Get the loading status of the chat registry
pub fn loading(&self) -> bool { pub fn loading(&self) -> bool {
self.tracking_flag.load(Ordering::Acquire) self.tracking.load(Ordering::Acquire)
} }
/// Get a weak reference to a room by its ID. /// Get a weak reference to a room by its ID.
@@ -491,7 +492,7 @@ impl ChatRegistry {
self.event_map self.event_map
.read_blocking() .read_blocking()
.get(id) .get(id)
.map(|(id, _dekey)| self.seen_on(id)) .map(|id| self.seen_on(id))
} }
/// Get the relays that have seen a given gift wrap id. /// Get the relays that have seen a given gift wrap id.
@@ -503,15 +504,6 @@ impl ChatRegistry {
.unwrap_or_default() .unwrap_or_default()
} }
/// Check if a given rumor was encrypted by the dekey.
pub fn encrypted_by_dekey(&self, id: &EventId) -> bool {
self.event_map
.read_blocking()
.get(id)
.map(|(_, dekey)| *dekey)
.unwrap_or(false)
}
/// Add a new room to the start of list. /// Add a new room to the start of list.
pub fn add_room<I>(&mut self, room: I, cx: &mut Context<Self>) pub fn add_room<I>(&mut self, room: I, cx: &mut Context<Self>)
where where

View File

@@ -470,12 +470,6 @@ impl ChatPanel {
self.reports_by_id.read(cx).get(id).is_some() self.reports_by_id.read(cx).get(id).is_some()
} }
/// Check if a message was encrypted by the dekey
fn encrypted_by_dekey(&self, id: &EventId, cx: &App) -> bool {
let chat = ChatRegistry::global(cx);
chat.read(cx).encrypted_by_dekey(id)
}
/// Get all sent reports for a message by its ID /// Get all sent reports for a message by its ID
fn sent_reports(&self, id: &EventId, cx: &App) -> Option<Vec<SendReport>> { fn sent_reports(&self, id: &EventId, cx: &App) -> Option<Vec<SendReport>> {
self.reports_by_id.read(cx).get(id).cloned() self.reports_by_id.read(cx).get(id).cloned()
@@ -848,7 +842,6 @@ impl ChatPanel {
let replies = message.replies_to.as_slice(); let replies = message.replies_to.as_slice();
let has_replies = !replies.is_empty(); let has_replies = !replies.is_empty();
let has_reports = self.has_reports(&id, cx); let has_reports = self.has_reports(&id, cx);
let encrypted_by_dekey = self.encrypted_by_dekey(&id, cx);
// Hide avatar setting // Hide avatar setting
let hide_avatar = AppSettings::get_hide_avatar(cx); let hide_avatar = AppSettings::get_hide_avatar(cx);
@@ -894,17 +887,6 @@ impl ChatPanel {
.text_color(cx.theme().text) .text_color(cx.theme().text)
.child(author.name()), .child(author.name()),
) )
.when(encrypted_by_dekey, |this| {
this.child(
Button::new(format!("dekey-{ix}"))
.icon(IconName::Shield)
.ghost()
.xsmall()
.px_4()
.tooltip("Encrypted by Dekey")
.disabled(true),
)
})
.child(message.created_at.to_human_time()) .child(message.created_at.to_human_time())
.when(has_reports, |this| { .when(has_reports, |this| {
this.child(deferred(self.render_sent_reports(&id, cx))) this.child(deferred(self.render_sent_reports(&id, cx)))

View File

@@ -298,10 +298,10 @@ impl Render for Notification {
let action = self.action_builder.clone().map(|builder| { let action = self.action_builder.clone().map(|builder| {
builder(self, window, cx) builder(self, window, cx)
.xsmall() .small()
.primary() .primary()
.px_3() .px_4()
.font_semibold() .font_medium()
}); });
let icon = match self.kind { let icon = match self.kind {
@@ -364,8 +364,14 @@ impl Render for Notification {
}) })
.when_some(content, |this, content| this.child(content)) .when_some(content, |this, content| this.child(content))
.when_some(action, |this, action| { .when_some(action, |this, action| {
this.gap_2() this.gap_2().child(
.child(h_flex().w_full().flex_1().justify_end().child(action)) h_flex()
.mt_2()
.w_full()
.flex_1()
.justify_end()
.child(action),
)
}), }),
) )
.child( .child(

View File

@@ -42,6 +42,7 @@ pub fn init(window: &mut Window, cx: &mut App) -> Entity<Workspace> {
struct DeviceNotifcation; struct DeviceNotifcation;
struct SignerNotifcation; struct SignerNotifcation;
struct RelayNotifcation; struct RelayNotifcation;
struct MsgRelayNotification;
#[derive(Action, Clone, PartialEq, Eq, Deserialize)] #[derive(Action, Clone, PartialEq, Eq, Deserialize)]
#[action(namespace = workspace, no_json)] #[action(namespace = workspace, no_json)]
@@ -184,6 +185,27 @@ impl Workspace {
// Observe all events emitted by the chat registry // Observe all events emitted by the chat registry
cx.subscribe_in(&chat, window, move |this, chat, ev, window, cx| { cx.subscribe_in(&chat, window, move |this, chat, ev, window, cx| {
match ev { match ev {
ChatEvent::InboxRelayNotFound => {
const MSG: &str = "Messaging Relays not found. Cannot receive messages.";
window.push_notification(
Notification::warning(MSG)
.id::<MsgRelayNotification>()
.autohide(false)
.action(|_this, _window, _cx| {
Button::new("retry").label("Retry").on_click(
move |_this, window, cx| {
let chat = ChatRegistry::global(cx);
chat.update(cx, |this, cx| {
this.get_metadata(cx);
});
window.clear_notification::<MsgRelayNotification>(cx);
},
)
}),
cx,
);
}
ChatEvent::OpenRoom(id) => { ChatEvent::OpenRoom(id) => {
if let Some(room) = chat.read(cx).room(id, cx) { if let Some(room) = chat.read(cx).room(id, cx) {
this.dock.update(cx, |this, cx| { this.dock.update(cx, |this, cx| {