diff --git a/crates/chats/src/room.rs b/crates/chats/src/room.rs index c867400..e7bb896 100644 --- a/crates/chats/src/room.rs +++ b/crates/chats/src/room.rs @@ -1,13 +1,14 @@ use std::sync::Arc; use account::Account; -use anyhow::{anyhow, Error}; +use anyhow::Error; use chrono::{Local, TimeZone}; use common::{compare, profile::SharedProfile, room_hash}; use global::get_client; use gpui::{App, AppContext, Context, EventEmitter, SharedString, Task, Window}; use itertools::Itertools; use nostr_sdk::prelude::*; +use smol::channel::Receiver; use crate::{ constants::{DAYS_IN_MONTH, HOURS_IN_DAY, MINUTES_IN_HOUR, NOW, SECONDS_IN_MINUTE}, @@ -20,6 +21,12 @@ pub struct IncomingEvent { pub event: RoomMessage, } +#[derive(Debug)] +pub enum SendStatus { + Sent(EventId), + Failed(Error), +} + #[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, Default)] pub enum RoomKind { Ongoing, @@ -368,20 +375,17 @@ impl Room { /// /// A Task that resolves to Result, Error> where the /// strings contain error messages for any failed sends - pub fn send_message(&self, content: String, cx: &App) -> Task, Error>> { - let account = Account::global(cx).read(cx); - - let Some(profile) = account.profile.clone() else { - return Task::ready(Err(anyhow!("User is not logged in"))); - }; - + pub fn send_message(&self, content: String, cx: &App) -> Option> { + let profile = Account::global(cx).read(cx).profile.clone()?; let public_key = profile.public_key(); + let subject = self.subject.clone(); let pubkeys = self.members.clone(); + let (tx, rx) = smol::channel::bounded::(pubkeys.len()); + cx.background_spawn(async move { let client = get_client(); - let mut report = vec![]; let mut tags: Vec = pubkeys .iter() @@ -401,16 +405,26 @@ impl Room { } for pubkey in pubkeys.iter() { - if let Err(e) = client + match client .send_private_msg(*pubkey, &content, tags.clone()) .await { - report.push(e.to_string()); + Ok(output) => { + if let Err(e) = tx.send(SendStatus::Sent(output.val)).await { + log::error!("Failed to send message to {}: {}", pubkey, e); + } + } + Err(e) => { + if let Err(e) = tx.send(SendStatus::Failed(e.into())).await { + log::error!("Failed to send message to {}: {}", pubkey, e); + } + } } } - - Ok(report) }) + .detach(); + + Some(rx) } /// Loads all messages for this room from the database diff --git a/crates/coop/src/views/chat.rs b/crates/coop/src/views/chat.rs index 8049770..1981937 100644 --- a/crates/coop/src/views/chat.rs +++ b/crates/coop/src/views/chat.rs @@ -1,6 +1,10 @@ use anyhow::{anyhow, Error}; use async_utility::task::spawn; -use chats::{message::RoomMessage, room::Room, ChatRegistry}; +use chats::{ + message::RoomMessage, + room::{Room, SendStatus}, + ChatRegistry, +}; use common::{nip96_upload, profile::SharedProfile}; use global::{constants::IMAGE_SERVICE, get_client}; use gpui::{ @@ -221,11 +225,13 @@ impl Chat { content = format!("{}\n{}", content, merged) } + // Check if content is empty if content.is_empty() { window.push_notification("Cannot send an empty message", cx); return; } + // Update input state self.input.update(cx, |this, cx| { this.set_loading(true, window, cx); this.set_disabled(true, window, cx); @@ -234,35 +240,43 @@ impl Chat { let room = self.room.read(cx); let task = room.send_message(content, cx); - cx.spawn_in(window, async move |this, cx| match task.await { - Ok(reports) => { - cx.update(|window, cx| { - this.update(cx, |this, cx| { - this.input.update(cx, |this, cx| { - this.set_loading(false, window, cx); - this.set_disabled(false, window, cx); - this.set_text("", window, cx); - }); + cx.spawn_in(window, async move |this, cx| { + let mut received = false; + + match task { + Some(rx) => { + while let Ok(message) = rx.recv().await { + if let SendStatus::Failed(error) = message { + cx.update(|window, cx| { + window.push_notification( + Notification::error(error.to_string()) + .title("Message Failed to Send"), + cx, + ); + }) + .ok(); + } else if !received { + cx.update(|window, cx| { + this.update(cx, |this, cx| { + this.input.update(cx, |this, cx| { + this.set_loading(false, window, cx); + this.set_disabled(false, window, cx); + this.set_text("", window, cx); + }); + received = true; + }) + .ok(); + }) + .ok(); + } + } + } + None => { + cx.update(|window, cx| { + window.push_notification(Notification::error("User is not logged in"), cx); }) .ok(); - - for item in reports.into_iter() { - window.push_notification( - Notification::error(item).title("Message Failed to Send"), - cx, - ); - } - }) - .ok(); - } - Err(e) => { - cx.update(|window, cx| { - window.push_notification( - Notification::error(e.to_string()).title("Message Failed to Send"), - cx, - ); - }) - .ok(); + } } }) .detach();