chore: improve ui responsiveness when send message

This commit is contained in:
2025-04-23 09:05:50 +07:00
parent 73b2eac080
commit 17251be3fd
2 changed files with 69 additions and 41 deletions

View File

@@ -1,13 +1,14 @@
use std::sync::Arc; use std::sync::Arc;
use account::Account; use account::Account;
use anyhow::{anyhow, Error}; use anyhow::Error;
use chrono::{Local, TimeZone}; use chrono::{Local, TimeZone};
use common::{compare, profile::SharedProfile, room_hash}; use common::{compare, profile::SharedProfile, room_hash};
use global::get_client; use global::get_client;
use gpui::{App, AppContext, Context, EventEmitter, SharedString, Task, Window}; use gpui::{App, AppContext, Context, EventEmitter, SharedString, Task, Window};
use itertools::Itertools; use itertools::Itertools;
use nostr_sdk::prelude::*; use nostr_sdk::prelude::*;
use smol::channel::Receiver;
use crate::{ use crate::{
constants::{DAYS_IN_MONTH, HOURS_IN_DAY, MINUTES_IN_HOUR, NOW, SECONDS_IN_MINUTE}, constants::{DAYS_IN_MONTH, HOURS_IN_DAY, MINUTES_IN_HOUR, NOW, SECONDS_IN_MINUTE},
@@ -20,6 +21,12 @@ pub struct IncomingEvent {
pub event: RoomMessage, pub event: RoomMessage,
} }
#[derive(Debug)]
pub enum SendStatus {
Sent(EventId),
Failed(Error),
}
#[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, Default)] #[derive(Clone, Copy, Hash, Debug, PartialEq, Eq, Default)]
pub enum RoomKind { pub enum RoomKind {
Ongoing, Ongoing,
@@ -368,20 +375,17 @@ impl Room {
/// ///
/// A Task that resolves to Result<Vec<String>, Error> where the /// A Task that resolves to Result<Vec<String>, Error> where the
/// strings contain error messages for any failed sends /// strings contain error messages for any failed sends
pub fn send_message(&self, content: String, cx: &App) -> Task<Result<Vec<String>, Error>> { pub fn send_message(&self, content: String, cx: &App) -> Option<Receiver<SendStatus>> {
let account = Account::global(cx).read(cx); let profile = Account::global(cx).read(cx).profile.clone()?;
let Some(profile) = account.profile.clone() else {
return Task::ready(Err(anyhow!("User is not logged in")));
};
let public_key = profile.public_key(); let public_key = profile.public_key();
let subject = self.subject.clone(); let subject = self.subject.clone();
let pubkeys = self.members.clone(); let pubkeys = self.members.clone();
let (tx, rx) = smol::channel::bounded::<SendStatus>(pubkeys.len());
cx.background_spawn(async move { cx.background_spawn(async move {
let client = get_client(); let client = get_client();
let mut report = vec![];
let mut tags: Vec<Tag> = pubkeys let mut tags: Vec<Tag> = pubkeys
.iter() .iter()
@@ -401,16 +405,26 @@ impl Room {
} }
for pubkey in pubkeys.iter() { for pubkey in pubkeys.iter() {
if let Err(e) = client match client
.send_private_msg(*pubkey, &content, tags.clone()) .send_private_msg(*pubkey, &content, tags.clone())
.await .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 /// Loads all messages for this room from the database

View File

@@ -1,6 +1,10 @@
use anyhow::{anyhow, Error}; use anyhow::{anyhow, Error};
use async_utility::task::spawn; 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 common::{nip96_upload, profile::SharedProfile};
use global::{constants::IMAGE_SERVICE, get_client}; use global::{constants::IMAGE_SERVICE, get_client};
use gpui::{ use gpui::{
@@ -221,11 +225,13 @@ impl Chat {
content = format!("{}\n{}", content, merged) content = format!("{}\n{}", content, merged)
} }
// Check if content is empty
if content.is_empty() { if content.is_empty() {
window.push_notification("Cannot send an empty message", cx); window.push_notification("Cannot send an empty message", cx);
return; return;
} }
// Update input state
self.input.update(cx, |this, cx| { self.input.update(cx, |this, cx| {
this.set_loading(true, window, cx); this.set_loading(true, window, cx);
this.set_disabled(true, window, cx); this.set_disabled(true, window, cx);
@@ -234,8 +240,22 @@ impl Chat {
let room = self.room.read(cx); let room = self.room.read(cx);
let task = room.send_message(content, cx); let task = room.send_message(content, cx);
cx.spawn_in(window, async move |this, cx| match task.await { cx.spawn_in(window, async move |this, cx| {
Ok(reports) => { 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| { cx.update(|window, cx| {
this.update(cx, |this, cx| { this.update(cx, |this, cx| {
this.input.update(cx, |this, cx| { this.input.update(cx, |this, cx| {
@@ -243,27 +263,21 @@ impl Chat {
this.set_disabled(false, window, cx); this.set_disabled(false, window, cx);
this.set_text("", window, cx); this.set_text("", window, cx);
}); });
received = true;
}) })
.ok(); .ok();
for item in reports.into_iter() {
window.push_notification(
Notification::error(item).title("Message Failed to Send"),
cx,
);
}
}) })
.ok(); .ok();
} }
Err(e) => { }
}
None => {
cx.update(|window, cx| { cx.update(|window, cx| {
window.push_notification( window.push_notification(Notification::error("User is not logged in"), cx);
Notification::error(e.to_string()).title("Message Failed to Send"),
cx,
);
}) })
.ok(); .ok();
} }
}
}) })
.detach(); .detach();
} }