optimize
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
pub use actions::*;
|
||||
@@ -59,6 +59,9 @@ pub struct ChatPanel {
|
||||
/// All messages (sorted by created_at)
|
||||
messages: Vec<Message>,
|
||||
|
||||
/// O(1) message lookup by EventId
|
||||
message_index: HashMap<EventId, usize>,
|
||||
|
||||
/// All reactions
|
||||
reactions: BTreeMap<EventId, Vec<(SharedString, PublicKey)>>,
|
||||
|
||||
@@ -166,6 +169,7 @@ impl ChatPanel {
|
||||
focus_handle: cx.focus_handle(),
|
||||
id,
|
||||
messages,
|
||||
message_index: HashMap::new(),
|
||||
reactions: BTreeMap::new(),
|
||||
room,
|
||||
list_state,
|
||||
@@ -231,23 +235,29 @@ impl ChatPanel {
|
||||
while let Ok(status) = rx.recv_async().await {
|
||||
{
|
||||
let mut map = reports.write().unwrap();
|
||||
for reports in map.values_mut() {
|
||||
for report in reports.iter_mut() {
|
||||
let status_id = match &*status {
|
||||
SendStatus::Ok { id, .. } => *id,
|
||||
SendStatus::Failed { id, .. } => *id,
|
||||
};
|
||||
|
||||
// Find the matching report and update it (exit early on first match)
|
||||
'outer: for reports_list in map.values_mut() {
|
||||
for report in reports_list.iter_mut() {
|
||||
let Some(output) = report.output.as_mut() else {
|
||||
continue;
|
||||
};
|
||||
if *output.id() != status_id {
|
||||
continue;
|
||||
}
|
||||
match &*status {
|
||||
SendStatus::Ok { id, relay } => {
|
||||
if output.id() == id {
|
||||
output.success.insert(relay.clone(), EventSendStatus::Sent);
|
||||
}
|
||||
SendStatus::Ok { relay, .. } => {
|
||||
output.success.insert(relay.clone(), EventSendStatus::Sent);
|
||||
}
|
||||
SendStatus::Failed { id, relay, message } => {
|
||||
if output.id() == id {
|
||||
output.failed.insert(relay.clone(), message.clone());
|
||||
}
|
||||
SendStatus::Failed { relay, message, .. } => {
|
||||
output.failed.insert(relay.clone(), message.clone());
|
||||
}
|
||||
}
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -472,6 +482,10 @@ impl ChatPanel {
|
||||
|
||||
if let Err(pos) = self.messages.binary_search(&msg) {
|
||||
self.messages.insert(pos, msg);
|
||||
// Rebuild message index after insertion (indices from pos to end shift)
|
||||
for (i, message) in self.messages.iter().enumerate().skip(pos) {
|
||||
self.message_index.insert(message.id, i);
|
||||
}
|
||||
self.list_state.splice(old_len..old_len, 1);
|
||||
|
||||
if scroll {
|
||||
@@ -514,22 +528,25 @@ impl ChatPanel {
|
||||
}
|
||||
|
||||
/// Check if a message has any reports
|
||||
fn has_reports(&self, id: &EventId, _cx: &App) -> bool {
|
||||
self.reports_by_id.read().unwrap().get(id).is_some()
|
||||
fn has_reports(&self, id: &EventId) -> bool {
|
||||
self.reports_by_id.read().unwrap().contains_key(id)
|
||||
}
|
||||
|
||||
fn sent_reports(&self, id: &EventId, _cx: &App) -> Option<Vec<SendReport>> {
|
||||
/// Clone reports for a message (used for modal display, not called during render)
|
||||
fn sent_reports(&self, id: &EventId) -> Option<Vec<SendReport>> {
|
||||
self.reports_by_id.read().unwrap().get(id).cloned()
|
||||
}
|
||||
|
||||
/// Get a message by its ID
|
||||
/// Get a message by its ID (O(1) lookup)
|
||||
fn message(&self, id: &EventId) -> Option<&Message> {
|
||||
self.messages.iter().find(|msg| &msg.id == id)
|
||||
self.message_index
|
||||
.get(id)
|
||||
.and_then(|&ix| self.messages.get(ix))
|
||||
}
|
||||
|
||||
/// Get a reaction by its target ID
|
||||
fn reaction(&self, id: &EventId) -> Vec<(SharedString, PublicKey)> {
|
||||
self.reactions.get(id).cloned().unwrap_or_default()
|
||||
/// Get a reaction by its target ID (returns reference, no allocation)
|
||||
fn reaction(&self, id: &EventId) -> &[(SharedString, PublicKey)] {
|
||||
self.reactions.get(id).map(|v| v.as_slice()).unwrap_or(&[])
|
||||
}
|
||||
|
||||
/// Check if a message has any reactions
|
||||
@@ -912,7 +929,7 @@ impl ChatPanel {
|
||||
let replies = message.replies_to.as_slice();
|
||||
let has_replies = !replies.is_empty();
|
||||
let has_reactions = self.has_reaction(&id);
|
||||
let has_reports = self.has_reports(&id, cx);
|
||||
let has_reports = self.has_reports(&id);
|
||||
|
||||
// Hide avatar setting
|
||||
let hide_avatar = AppSettings::get_hide_avatar(cx);
|
||||
@@ -1095,7 +1112,7 @@ impl ChatPanel {
|
||||
|
||||
// Group reactions by emoji and collect authors for each
|
||||
let mut grouped: BTreeMap<SharedString, Vec<PublicKey>> = BTreeMap::new();
|
||||
for (emoji, author) in &reactions {
|
||||
for (emoji, author) in reactions {
|
||||
grouped.entry(emoji.clone()).or_default().push(*author);
|
||||
}
|
||||
|
||||
@@ -1127,7 +1144,7 @@ impl ChatPanel {
|
||||
}
|
||||
|
||||
fn render_sent_reports(&self, id: &EventId, cx: &App) -> impl IntoElement {
|
||||
let reports = self.sent_reports(id, cx);
|
||||
let reports = self.sent_reports(id);
|
||||
|
||||
let pending = reports
|
||||
.as_ref()
|
||||
|
||||
Reference in New Issue
Block a user