This commit is contained in:
2026-07-27 10:46:10 +07:00
parent 8bbb472103
commit b349656c56
3 changed files with 168 additions and 226 deletions

View File

@@ -1,7 +1,7 @@
use std::collections::{BTreeMap, BTreeSet, HashSet};
use std::sync::Arc;
#[cfg(target_arch = "wasm32")]
use std::sync::RwLock;
use std::sync::RwLock as AsyncRwLock;
use std::sync::{Arc, RwLock};
pub use actions::*;
use anyhow::{Context as AnyhowContext, Error};
@@ -21,7 +21,7 @@ use person::{Person, PersonRegistry};
use settings::{AppSettings, SignerKind};
use smallvec::{SmallVec, smallvec};
#[cfg(not(target_arch = "wasm32"))]
use smol::lock::RwLock;
use smol::lock::RwLock as AsyncRwLock;
use state::{NostrRegistry, upload};
use theme::ActiveTheme;
use ui::avatar::Avatar;
@@ -63,7 +63,7 @@ pub struct ChatPanel {
rendered_texts_by_id: BTreeMap<EventId, RenderedText>,
/// Mapping message (rumor event) ids to their reports
reports_by_id: Entity<BTreeMap<EventId, Vec<SendReport>>>,
reports_by_id: Arc<RwLock<BTreeMap<EventId, Vec<SendReport>>>>,
/// Chat input state
input: Entity<InputState>,
@@ -75,7 +75,7 @@ pub struct ChatPanel {
subject_bar: Entity<bool>,
/// Sent message ids
sent_ids: Arc<RwLock<Vec<EventId>>>,
sent_ids: Arc<AsyncRwLock<Vec<EventId>>>,
/// Replies to
replies_to: Entity<HashSet<EventId>>,
@@ -98,7 +98,7 @@ impl ChatPanel {
// Define attachments and replies_to entities
let attachments = cx.new(|_| vec![]);
let replies_to = cx.new(|_| HashSet::new());
let reports_by_id = cx.new(|_| BTreeMap::new());
let reports_by_id = Arc::new(RwLock::new(BTreeMap::new()));
// Define list of messages
let messages = BTreeSet::default();
@@ -172,7 +172,7 @@ impl ChatPanel {
attachments,
rendered_texts_by_id: BTreeMap::new(),
reports_by_id,
sent_ids: Arc::new(RwLock::new(Vec::new())),
sent_ids: Arc::new(AsyncRwLock::new(Vec::new())),
uploading: false,
subscriptions,
tasks: vec![],
@@ -192,7 +192,7 @@ impl ChatPanel {
let nostr = NostrRegistry::global(cx);
let client = nostr.read(cx).client();
let sent_ids = self.sent_ids.clone();
let reports = self.reports_by_id.downgrade();
let reports = self.reports_by_id.clone();
let (tx, rx) = flume::bounded::<Arc<SendStatus>>(256);
@@ -223,10 +223,11 @@ impl ChatPanel {
Ok(())
}));
self.tasks.push(cx.spawn(async move |_this, cx| {
self.tasks.push(cx.spawn(async move |this, cx| {
while let Ok(status) = rx.recv_async().await {
reports.update(cx, |this, cx| {
for reports in this.values_mut() {
{
let mut map = reports.write().unwrap();
for reports in map.values_mut() {
for report in reports.iter_mut() {
let Some(output) = report.output.as_mut() else {
continue;
@@ -243,10 +244,10 @@ impl ChatPanel {
}
}
}
cx.notify();
}
}
})?;
}
this.update(cx, |_, cx| cx.notify()).ok();
}
Ok(())
}));
@@ -345,69 +346,46 @@ impl ChatPanel {
return;
}
// Get room entity
let room = self.room.clone();
// Get content and replies
let replies: Vec<EventId> = self.replies_to.read(cx).iter().copied().collect();
let content = value.to_string();
self.tasks.push(cx.spawn_in(window, async move |this, cx| {
let room = room.upgrade().context("Room is not available")?;
this.update_in(cx, |this, window, cx| {
match room.read(cx).rumor(content, replies, cx) {
Some(rumor) => {
this.insert_message(&rumor, true, cx);
this.send_and_wait(rumor, window, cx);
this.clear(window, cx);
}
None => {
window.push_notification("Failed to create message", cx);
}
}
})?;
Ok(())
}));
}
/// Send message in the background and wait for the response
fn send_and_wait(&mut self, rumor: UnsignedEvent, window: &mut Window, cx: &mut Context<Self>) {
let sent_ids = self.sent_ids.clone();
// This can't fail, because we already ensured that the ID is set
let id = rumor.id.unwrap();
// Add empty reports
self.insert_reports(id, vec![], cx);
// Upgrade room reference
let Some(room) = self.room.upgrade() else {
// Upgrade room and create the rumor synchronously
let Some(room_entity) = room.upgrade() else {
return;
};
let Some(rumor) = room_entity.read(cx).rumor(content.clone(), replies, cx) else {
window.push_notification("Failed to create message", cx);
return;
};
// Get the send message task
let Some(task) = room.read(cx).send(rumor, cx) else {
let id = rumor.id.expect("rumor must have an id");
// Insert optimistic message and clear input
self.insert_message(&rumor, true, cx);
self.insert_reports(id, vec![], cx);
self.clear(window, cx);
// Get the send task
let Some(send_task) = room_entity.read(cx).send(rumor, cx) else {
window.push_notification("Failed to send message", cx);
return;
};
// Spawn a single task to await the send and update reports
self.tasks.push(cx.spawn_in(window, async move |this, cx| {
// Send and get reports
let outputs = task.await;
let outputs = send_task.await;
// Add sent IDs to the list
let mut sent_ids = sent_ids.write().await;
sent_ids.extend(outputs.iter().filter_map(|output| output.gift_wrap_id));
// Update the state
this.update(cx, |this, cx| {
this.insert_reports(id, outputs, cx);
})?;
Ok(())
}))
}));
}
/// Clear the input field, attachments, and replies
@@ -429,10 +407,13 @@ impl ChatPanel {
/// Insert reports
fn insert_reports(&mut self, id: EventId, reports: Vec<SendReport>, cx: &mut Context<Self>) {
self.reports_by_id.update(cx, |this, cx| {
this.entry(id).or_default().extend(reports);
cx.notify();
});
self.reports_by_id
.write()
.unwrap()
.entry(id)
.or_default()
.extend(reports);
cx.notify();
}
/// Insert a message into the chat panel
@@ -466,13 +447,12 @@ impl ChatPanel {
}
/// Check if a message has any reports
fn has_reports(&self, id: &EventId, cx: &App) -> bool {
self.reports_by_id.read(cx).get(id).is_some()
fn has_reports(&self, id: &EventId, _cx: &App) -> bool {
self.reports_by_id.read().unwrap().get(id).is_some()
}
/// Get all sent reports for a message by its ID
fn sent_reports(&self, id: &EventId, cx: &App) -> Option<Vec<SendReport>> {
self.reports_by_id.read(cx).get(id).cloned()
fn sent_reports(&self, id: &EventId, _cx: &App) -> Option<Vec<SendReport>> {
self.reports_by_id.read().unwrap().get(id).cloned()
}
/// Get a message by its ID