diff --git a/crates/chat/src/lib.rs b/crates/chat/src/lib.rs index 2c35522..f5346e7 100644 --- a/crates/chat/src/lib.rs +++ b/crates/chat/src/lib.rs @@ -16,7 +16,7 @@ use gpui::{ use nostr_sdk::prelude::*; use smallvec::{SmallVec, smallvec}; 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 room; @@ -41,6 +41,8 @@ pub enum ChatEvent { CloseRoom(u64), /// An event to notify UI about a new chat request Ping, + /// No Inbox Relays found, the app is not ready to subscribe messages + InboxRelayNotFound, /// An error occurred Error(SharedString), } @@ -85,9 +87,6 @@ impl Signal { } } -type Dekey = bool; -type GiftWrapId = EventId; - /// Chat Registry #[derive(Debug)] pub struct ChatRegistry { @@ -101,10 +100,13 @@ pub struct ChatRegistry { seens: Arc>>>, /// Mapping of unwrapped event ids to their gift wrap event ids - event_map: Arc>>, + event_map: Arc>>, /// Tracking the status of unwrapping gift wrap events. - tracking_flag: Arc, + tracking: Arc, + + /// Whether the messaging relays have been found. + msg_relays_existed: Arc, /// Channel for sending signals to the UI. signal_tx: flume::Sender, @@ -162,7 +164,8 @@ impl ChatRegistry { trashes: cx.new(|_| BTreeSet::default()), seens: 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_tx: tx, tasks: smallvec![], @@ -175,7 +178,10 @@ impl ChatRegistry { let nostr = NostrRegistry::global(cx); let client = nostr.read(cx).client(); 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 event_map = self.event_map.clone(); let trashes = self.trashes.downgrade(); @@ -199,10 +205,7 @@ impl ChatRegistry { }; match *message { - RelayMessage::Event { - event, - subscription_id, - } => { + RelayMessage::Event { event, .. } => { // Keep track of which relays have seen this event { let mut seens = seens.write().await; @@ -219,6 +222,9 @@ impl ChatRegistry { let current_user = signer.get_public_key().await?; 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?; } } @@ -234,8 +240,7 @@ impl ChatRegistry { // Map the rumor id to the gift wrap event id for later lookup { let mut event_map = event_map.write().await; - let dekey = subscription_id.as_ref() == &sub_id1; - event_map.insert(rumor.id.unwrap(), (event.id, dekey)); + event_map.insert(rumor.id.unwrap(), event.id); } // Check if the rumor has a recipient @@ -250,7 +255,7 @@ impl ChatRegistry { tx.send_async(signal).await?; } else { // Mark the chat still processing new messages - status.store(true, Ordering::Release); + tracking.store(true, Ordering::Release); } } Err(e) => { @@ -287,7 +292,7 @@ impl ChatRegistry { } Signal::InboxRelayNotFound => { this.update(cx, |_this, cx| { - cx.emit(ChatEvent::Error("Messaging Relays not found".into())); + cx.emit(ChatEvent::InboxRelayNotFound); })?; } Signal::Eose => { @@ -310,7 +315,7 @@ impl ChatRegistry { /// Tracking the status of unwrapping gift wrap events. fn tracking(&mut self, cx: &mut Context) { - let status = self.tracking_flag.clone(); + let status = self.tracking.clone(); let tx = self.signal_tx.clone(); self.tasks.push(cx.background_spawn(async move { @@ -328,8 +333,8 @@ impl ChatRegistry { })); } - /// Get contact list from relays - fn get_metadata(&mut self, cx: &mut Context) { + /// Get all necessary metadata from relays for current user + pub fn get_metadata(&mut self, cx: &mut Context) { let nostr = NostrRegistry::global(cx); let client = nostr.read(cx).client(); let signer = nostr.read(cx).signer(); @@ -339,9 +344,7 @@ impl ChatRegistry { }; self.tasks.push(cx.background_spawn(async move { - let opts = SubscribeAutoCloseOptions::default() - .exit_policy(ReqExitPolicy::ExitOnEOSE) - .timeout(Some(Duration::from_secs(TIMEOUT))); + let opts = SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE); // Construct filter for msg relays let msg_relays = Filter::new() @@ -358,29 +361,27 @@ impl ChatRegistry { // Subscribe client .subscribe(vec![msg_relays, contact_list]) + .with_id(SubscriptionId::new("user-meta")) .close_on(opts) .await?; Ok(()) })); - let client = nostr.read(cx).client(); 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 { - loop { - let filter = Filter::new() - .kind(Kind::InboxRelays) - .author(public_key) - .limit(1); + // Wait for 5 seconds + smol::Timer::after(Duration::from_secs(5)).await; - if client.database().query(filter).await?.first().is_some() { - break; - } else { - tx.send_async(Signal::inbox_relay_not_found()).await?; - } - - smol::Timer::after(Duration::from_secs(5)).await; + // Then check if the msg relays have been found + if !msg_relays_existed.load(Ordering::Acquire) { + tx.send_async(Signal::inbox_relay_not_found()).await?; } Ok(()) @@ -439,7 +440,7 @@ impl ChatRegistry { /// Get the loading status of the chat registry 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. @@ -491,7 +492,7 @@ impl ChatRegistry { self.event_map .read_blocking() .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. @@ -503,15 +504,6 @@ impl ChatRegistry { .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. pub fn add_room(&mut self, room: I, cx: &mut Context) where diff --git a/crates/chat_ui/src/lib.rs b/crates/chat_ui/src/lib.rs index c2d2a6d..43c07d4 100644 --- a/crates/chat_ui/src/lib.rs +++ b/crates/chat_ui/src/lib.rs @@ -470,12 +470,6 @@ impl ChatPanel { 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 fn sent_reports(&self, id: &EventId, cx: &App) -> Option> { self.reports_by_id.read(cx).get(id).cloned() @@ -848,7 +842,6 @@ impl ChatPanel { let replies = message.replies_to.as_slice(); let has_replies = !replies.is_empty(); let has_reports = self.has_reports(&id, cx); - let encrypted_by_dekey = self.encrypted_by_dekey(&id, cx); // Hide avatar setting let hide_avatar = AppSettings::get_hide_avatar(cx); @@ -894,17 +887,6 @@ impl ChatPanel { .text_color(cx.theme().text) .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()) .when(has_reports, |this| { this.child(deferred(self.render_sent_reports(&id, cx))) diff --git a/crates/ui/src/notification.rs b/crates/ui/src/notification.rs index bf720be..4779a37 100644 --- a/crates/ui/src/notification.rs +++ b/crates/ui/src/notification.rs @@ -298,10 +298,10 @@ impl Render for Notification { let action = self.action_builder.clone().map(|builder| { builder(self, window, cx) - .xsmall() + .small() .primary() - .px_3() - .font_semibold() + .px_4() + .font_medium() }); let icon = match self.kind { @@ -364,8 +364,14 @@ impl Render for Notification { }) .when_some(content, |this, content| this.child(content)) .when_some(action, |this, action| { - this.gap_2() - .child(h_flex().w_full().flex_1().justify_end().child(action)) + this.gap_2().child( + h_flex() + .mt_2() + .w_full() + .flex_1() + .justify_end() + .child(action), + ) }), ) .child( diff --git a/desktop/src/workspace.rs b/desktop/src/workspace.rs index f487b1b..0a001f4 100644 --- a/desktop/src/workspace.rs +++ b/desktop/src/workspace.rs @@ -42,6 +42,7 @@ pub fn init(window: &mut Window, cx: &mut App) -> Entity { struct DeviceNotifcation; struct SignerNotifcation; struct RelayNotifcation; +struct MsgRelayNotification; #[derive(Action, Clone, PartialEq, Eq, Deserialize)] #[action(namespace = workspace, no_json)] @@ -184,6 +185,27 @@ impl Workspace { // Observe all events emitted by the chat registry cx.subscribe_in(&chat, window, move |this, chat, ev, window, cx| { match ev { + ChatEvent::InboxRelayNotFound => { + const MSG: &str = "Messaging Relays not found. Cannot receive messages."; + + window.push_notification( + Notification::warning(MSG) + .id::() + .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::(cx); + }, + ) + }), + cx, + ); + } ChatEvent::OpenRoom(id) => { if let Some(room) = chat.read(cx).room(id, cx) { this.dock.update(cx, |this, cx| {