chore: refactor message sending (#172)

* refactor send message

* refactor resend

* fix

* refactor

* clean up
This commit is contained in:
reya
2025-09-30 08:59:38 +07:00
committed by GitHub
parent 880ba30d20
commit 0db48bc003
6 changed files with 337 additions and 322 deletions

View File

@@ -1,4 +1,5 @@
use std::collections::{HashMap, HashSet};
use std::time::Duration;
use common::display::{RenderedProfile, RenderedTimestamp};
use common::nip96::nip96_upload;
@@ -99,7 +100,7 @@ impl Chat {
let messages = BTreeSet::from([Message::system()]);
let list_state = ListState::new(messages.len(), ListAlignment::Bottom, px(1024.));
let connect_relays = room.read(cx).connect_relays(cx);
let connect = room.read(cx).connect(cx);
let load_messages = room.read(cx).load_messages(cx);
let mut subscriptions = smallvec![];
@@ -108,43 +109,41 @@ impl Chat {
tasks.push(
// Load all messages belonging to this room
cx.spawn_in(window, async move |this, cx| {
match connect_relays.await {
Ok(relays) => {
this.update(cx, |this, cx| {
this.relays.update(cx, |this, cx| {
*this = relays;
cx.notify();
});
})
.ok();
}
Err(e) => {
cx.update(|window, cx| {
let result = load_messages.await;
this.update_in(cx, |this, window, cx| {
match result {
Ok(events) => {
this.insert_messages(events, cx);
}
Err(e) => {
window.push_notification(e.to_string(), cx);
})
.ok();
}
};
}
};
})
.ok();
}),
);
tasks.push(
// Load all messages belonging to this room
// Get messaging relays for all members
cx.spawn_in(window, async move |this, cx| {
match load_messages.await {
Ok(events) => {
this.update(cx, |this, cx| {
this.insert_messages(events, cx);
})
.ok();
}
Err(e) => {
cx.update(|window, cx| {
window.push_notification(e.to_string(), cx);
})
.ok();
}
};
let result = connect.await;
this.update_in(cx, |this, _window, cx| {
match result {
Ok(relays) => {
this.relays.update(cx, |this, cx| {
this.extend(relays);
cx.notify();
});
}
Err(e) => {
this.insert_warning(e.to_string(), cx);
}
};
})
.ok();
}),
);
@@ -192,9 +191,12 @@ impl Chat {
subscriptions.push(
// Observe the messaging relays of the room's members
cx.observe_in(&relays, window, |this, entity, _window, cx| {
for (public_key, urls) in entity.read(cx).clone().into_iter() {
let registry = Registry::global(cx);
let relays = entity.read(cx).clone();
for (public_key, urls) in relays.iter() {
if urls.is_empty() {
let profile = Registry::read_global(cx).get_person(&public_key, cx);
let profile = registry.read(cx).get_person(public_key, cx);
let content = t!("chat.nip17_not_found", u = profile.name());
this.insert_warning(content, cx);
@@ -206,7 +208,6 @@ impl Chat {
subscriptions.push(
// Observe when user close chat panel
cx.on_release_in(window, move |this, window, cx| {
this.disconnect_relays(cx);
this.messages.clear();
this.rendered_texts_by_id.clear();
this.reports_by_id.clear();
@@ -235,20 +236,6 @@ impl Chat {
}
}
/// Disconnect all relays when the user closes the chat panel
fn disconnect_relays(&mut self, cx: &mut App) {
let relays = self.relays.read(cx).clone();
cx.background_spawn(async move {
let client = nostr_client();
for relay in relays.values().flatten() {
client.disconnect_relay(relay).await.ok();
}
})
.detach();
}
/// Load all messages belonging to this room
fn load_messages(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let load_messages = self.room.read(cx).load_messages(cx);
@@ -273,11 +260,6 @@ impl Chat {
);
}
#[allow(dead_code)]
fn mention_popup(&mut self, _text: &str, _input: &Entity<InputState>, _cx: &mut Context<Self>) {
// TODO: open mention popup at current cursor position
}
/// Get user input content and merged all attachments
fn input_content(&self, cx: &Context<Self>) -> String {
let mut content = self.input.read(cx).value().trim().to_string();
@@ -313,36 +295,48 @@ impl Chat {
return;
}
// Temporary disable the message input
self.input.update(cx, |this, cx| {
this.set_loading(false, cx);
this.set_disabled(false, cx);
this.set_value("", window, cx);
});
// Get the backup setting
let backup = AppSettings::get_backup_messages(cx);
// Get replies_to if it's present
let replies = self.replies_to.read(cx).iter().copied().collect_vec();
let replies: Vec<EventId> = self.replies_to.read(cx).iter().copied().collect();
// Get the current room entity
let room = self.room.read(cx);
let identity = Registry::read_global(cx).identity(cx).public_key();
// Create a temporary message for optimistic update
let temp_message = room.create_temp_message(identity, &content, replies.as_ref());
let temp_id = temp_message.id.unwrap();
let rumor = room.create_message(&content, replies.as_ref(), cx);
let rumor_id = rumor.id.unwrap();
// Create a task for sending the message in the background
let send_message = room.send_in_background(&content, replies, backup, cx);
let send_message = room.send_message(rumor.clone(), backup, cx);
// Optimistically update message list
self.insert_message(Message::user(temp_message), true, cx);
cx.spawn_in(window, async move |this, cx| {
cx.background_executor()
.timer(Duration::from_millis(100))
.await;
// Remove all replies
self.remove_all_replies(cx);
// remove all attachments
self.remove_all_attachments(cx);
// Reset the input state
self.input.update(cx, |this, cx| {
this.set_value("", window, cx);
});
this.update_in(cx, |this, window, cx| {
this.insert_message(Message::user(rumor), true, cx);
this.remove_all_replies(cx);
this.remove_all_attachments(cx);
this.input.update(cx, |this, cx| {
this.set_loading(false, cx);
this.set_disabled(false, cx);
this.set_value("", window, cx);
});
})
.ok();
})
.detach();
// Continue sending the message in the background
cx.spawn_in(window, async move |this, cx| {
@@ -363,7 +357,7 @@ impl Chat {
});
// Insert the sent reports
this.reports_by_id.insert(temp_id, reports);
this.reports_by_id.insert(rumor_id, reports);
cx.notify();
}
@@ -377,37 +371,31 @@ impl Chat {
.detach();
}
/// Resend a failed message
fn resend_message(&mut self, id: &EventId, window: &mut Window, cx: &mut Context<Self>) {
if let Some(reports) = self.reports_by_id.get(id).cloned() {
if let Some(message) = self.message(id) {
let backup = AppSettings::get_backup_messages(cx);
let id_clone = id.to_owned();
let message = message.content.to_owned();
let task = self.room.read(cx).resend(reports, message, backup, cx);
let id_clone = id.to_owned();
let resend = self.room.read(cx).resend_message(reports, cx);
cx.spawn_in(window, async move |this, cx| {
match task.await {
cx.spawn_in(window, async move |this, cx| {
let result = resend.await;
this.update_in(cx, |this, window, cx| {
match result {
Ok(reports) => {
if !reports.is_empty() {
this.update(cx, |this, cx| {
this.reports_by_id.entry(id_clone).and_modify(|this| {
*this = reports;
});
cx.notify();
})
.ok();
}
this.reports_by_id.entry(id_clone).and_modify(|this| {
*this = reports;
});
cx.notify();
}
Err(e) => {
cx.update(|window, cx| {
window.push_notification(e.to_string(), cx);
})
.ok();
window.push_notification(Notification::error(e.to_string()), cx);
}
};
})
.detach();
}
.ok();
})
.detach();
}
}
@@ -612,7 +600,7 @@ impl Chat {
});
}
fn render_announcement(&mut self, ix: usize, cx: &mut Context<Self>) -> AnyElement {
fn render_announcement(&self, ix: usize, cx: &Context<Self>) -> AnyElement {
v_flex()
.id(ix)
.group("")
@@ -638,7 +626,7 @@ impl Chat {
.into_any_element()
}
fn render_warning(&mut self, ix: usize, content: String, cx: &mut Context<Self>) -> AnyElement {
fn render_warning(&self, ix: usize, content: SharedString, cx: &Context<Self>) -> AnyElement {
div()
.id(ix)
.relative()
@@ -652,7 +640,7 @@ impl Chat {
.text_sm()
.text_color(cx.theme().warning_foreground)
.child(Avatar::new("brand/system.png").size(rems(2.)))
.child(SharedString::from(content)),
.child(content),
)
.child(
div()
@@ -666,23 +654,6 @@ impl Chat {
.into_any_element()
}
fn render_message_not_found(&self, ix: usize, cx: &Context<Self>) -> AnyElement {
div()
.id(ix)
.w_full()
.py_1()
.px_3()
.child(
h_flex()
.gap_1()
.text_xs()
.text_color(cx.theme().danger_foreground)
.child(SharedString::from(ix.to_string()))
.child(shared_t!("chat.not_found")),
)
.into_any_element()
}
fn render_message(
&self,
ix: usize,
@@ -1237,8 +1208,7 @@ impl Chat {
weak_view.read_with(cx, |this, cx| this.new_subject(cx))
{
room.update(cx, |this, cx| {
this.subject = Some(subject);
cx.notify();
this.set_subject(subject, cx);
})
.ok();
}
@@ -1381,13 +1351,13 @@ impl Render for Chat {
this.render_message(ix, rendered, text, cx)
}
Message::Warning(content, _) => {
this.render_warning(ix, content.to_owned(), cx)
Message::Warning(content, _timestamp) => {
this.render_warning(ix, SharedString::from(content), cx)
}
Message::System(_) => this.render_announcement(ix, cx),
Message::System(_timestamp) => this.render_announcement(ix, cx),
}
} else {
this.render_message_not_found(ix, cx)
this.render_warning(ix, shared_t!("chat.not_found"), cx)
}
}),
)