chore: fix duplicate messages (#108)
* prevent duplicate message on load * refactor
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
use std::cell::RefCell;
|
use std::collections::{BTreeSet, HashMap};
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
@@ -57,7 +56,7 @@ pub struct Chat {
|
|||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
// Chat Room
|
// Chat Room
|
||||||
room: Entity<Room>,
|
room: Entity<Room>,
|
||||||
messages: Entity<Vec<Rc<RefCell<Message>>>>,
|
messages: Entity<BTreeSet<Message>>,
|
||||||
text_data: HashMap<EventId, RichText>,
|
text_data: HashMap<EventId, RichText>,
|
||||||
list_state: ListState,
|
list_state: ListState,
|
||||||
// New Message
|
// New Message
|
||||||
@@ -76,7 +75,7 @@ impl Chat {
|
|||||||
pub fn new(room: Entity<Room>, window: &mut Window, cx: &mut App) -> Entity<Self> {
|
pub fn new(room: Entity<Room>, window: &mut Window, cx: &mut App) -> Entity<Self> {
|
||||||
let attaches = cx.new(|_| None);
|
let attaches = cx.new(|_| None);
|
||||||
let replies_to = cx.new(|_| None);
|
let replies_to = cx.new(|_| None);
|
||||||
let messages = cx.new(|_| vec![]);
|
let messages = cx.new(|_| BTreeSet::new());
|
||||||
|
|
||||||
let input = cx.new(|cx| {
|
let input = cx.new(|cx| {
|
||||||
InputState::new(window, cx)
|
InputState::new(window, cx)
|
||||||
@@ -120,10 +119,10 @@ impl Chat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let old_len = this.messages.read(cx).len();
|
let old_len = this.messages.read(cx).len();
|
||||||
let message = event.clone().into_rc();
|
let message = event.to_owned();
|
||||||
|
|
||||||
cx.update_entity(&this.messages, |this, cx| {
|
cx.update_entity(&this.messages, |this, cx| {
|
||||||
this.extend(vec![message]);
|
this.insert(message);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -179,7 +178,7 @@ impl Chat {
|
|||||||
|
|
||||||
// Extend the messages list with the new events
|
// Extend the messages list with the new events
|
||||||
this.messages.update(cx, |this, cx| {
|
this.messages.update(cx, |this, cx| {
|
||||||
this.extend(messages.into_iter().map(|e| e.into_rc()));
|
this.extend(messages);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -242,9 +241,8 @@ impl Chat {
|
|||||||
self.messages
|
self.messages
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.iter()
|
.iter()
|
||||||
.filter(|m| m.borrow().author == identity)
|
.filter(|m| m.author == identity)
|
||||||
.any(|existing| {
|
.any(|existing| {
|
||||||
let existing = existing.borrow();
|
|
||||||
// Check if messages are within the time window
|
// Check if messages are within the time window
|
||||||
(existing.created_at.as_u64() >= min_timestamp) &&
|
(existing.created_at.as_u64() >= min_timestamp) &&
|
||||||
// Compare content and author
|
// Compare content and author
|
||||||
@@ -316,10 +314,9 @@ impl Chat {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.messages.update(cx, |this, cx| {
|
this.messages.update(cx, |this, cx| {
|
||||||
if let Some(msg) =
|
if let Some(mut msg) = this.iter().find(|msg| msg.id == id).cloned()
|
||||||
this.iter().find(|msg| msg.borrow().id == id).cloned()
|
|
||||||
{
|
{
|
||||||
msg.borrow_mut().errors = Some(reports);
|
msg.errors = Some(reports);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -334,10 +331,9 @@ impl Chat {
|
|||||||
|
|
||||||
fn insert_message(&self, message: Message, cx: &mut Context<Self>) {
|
fn insert_message(&self, message: Message, cx: &mut Context<Self>) {
|
||||||
let old_len = self.messages.read(cx).len();
|
let old_len = self.messages.read(cx).len();
|
||||||
let message = message.into_rc();
|
|
||||||
|
|
||||||
cx.update_entity(&self.messages, |this, cx| {
|
cx.update_entity(&self.messages, |this, cx| {
|
||||||
this.extend(vec![message]);
|
this.insert(message);
|
||||||
cx.notify();
|
cx.notify();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -345,12 +341,7 @@ impl Chat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn scroll_to(&self, id: EventId, cx: &Context<Self>) {
|
fn scroll_to(&self, id: EventId, cx: &Context<Self>) {
|
||||||
if let Some(ix) = self
|
if let Some(ix) = self.messages.read(cx).iter().position(|m| m.id == id) {
|
||||||
.messages
|
|
||||||
.read(cx)
|
|
||||||
.iter()
|
|
||||||
.position(|m| m.borrow().id == id)
|
|
||||||
{
|
|
||||||
self.list_state.scroll_to_reveal_item(ix);
|
self.list_state.scroll_to_reveal_item(ix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,8 +350,9 @@ impl Chat {
|
|||||||
let Some(item) = self
|
let Some(item) = self
|
||||||
.messages
|
.messages
|
||||||
.read(cx)
|
.read(cx)
|
||||||
.get(ix)
|
.iter()
|
||||||
.map(|m| ClipboardItem::new_string(m.borrow().content.to_string()))
|
.nth(ix)
|
||||||
|
.map(|m| ClipboardItem::new_string(m.content.to_string()))
|
||||||
else {
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
@@ -369,7 +361,7 @@ impl Chat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn reply_to(&mut self, ix: usize, cx: &mut Context<Self>) {
|
fn reply_to(&mut self, ix: usize, cx: &mut Context<Self>) {
|
||||||
let Some(message) = self.messages.read(cx).get(ix).map(|m| m.borrow().clone()) else {
|
let Some(message) = self.messages.read(cx).iter().nth(ix).map(|m| m.to_owned()) else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -583,7 +575,7 @@ impl Chat {
|
|||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let Some(message) = self.messages.read(cx).get(ix).map(|m| m.borrow()) else {
|
let Some(message) = self.messages.read(cx).iter().nth(ix) else {
|
||||||
return div().id(ix);
|
return div().id(ix);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -642,10 +634,7 @@ impl Chat {
|
|||||||
let messages = self.messages.read(cx);
|
let messages = self.messages.read(cx);
|
||||||
|
|
||||||
for (ix, id) in replies.iter().cloned().enumerate() {
|
for (ix, id) in replies.iter().cloned().enumerate() {
|
||||||
let Some(message) = messages
|
let Some(message) = messages.iter().find(|m| m.id == id)
|
||||||
.iter()
|
|
||||||
.map(|m| m.borrow())
|
|
||||||
.find(|m| m.id == id)
|
|
||||||
else {
|
else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
use std::cell::RefCell;
|
use std::hash::Hash;
|
||||||
use std::iter::IntoIterator;
|
use std::iter::IntoIterator;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use chrono::{Local, TimeZone};
|
use chrono::{Local, TimeZone};
|
||||||
use gpui::SharedString;
|
use gpui::SharedString;
|
||||||
@@ -12,7 +11,7 @@ use crate::room::SendError;
|
|||||||
///
|
///
|
||||||
/// Contains information about the message content, author, creation time,
|
/// Contains information about the message content, author, creation time,
|
||||||
/// mentions, replies, and any errors that occurred during sending.
|
/// mentions, replies, and any errors that occurred during sending.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Message {
|
pub struct Message {
|
||||||
/// Unique identifier of the message (EventId from nostr_sdk)
|
/// Unique identifier of the message (EventId from nostr_sdk)
|
||||||
pub id: EventId,
|
pub id: EventId,
|
||||||
@@ -30,6 +29,32 @@ pub struct Message {
|
|||||||
pub errors: Option<Vec<SendError>>,
|
pub errors: Option<Vec<SendError>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Eq for Message {}
|
||||||
|
|
||||||
|
impl PartialEq for Message {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.id == other.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Ord for Message {
|
||||||
|
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||||
|
self.created_at.cmp(&other.created_at)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialOrd for Message {
|
||||||
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||||
|
Some(self.cmp(other))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Hash for Message {
|
||||||
|
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.id.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Builder pattern implementation for constructing Message objects.
|
/// Builder pattern implementation for constructing Message objects.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MessageBuilder {
|
pub struct MessageBuilder {
|
||||||
@@ -110,11 +135,6 @@ impl MessageBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the message wrapped in an Rc<RefCell<Message>>
|
|
||||||
pub fn build_rc(self) -> Result<Rc<RefCell<Message>>, String> {
|
|
||||||
self.build().map(|m| Rc::new(RefCell::new(m)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds the message
|
/// Builds the message
|
||||||
pub fn build(self) -> Result<Message, String> {
|
pub fn build(self) -> Result<Message, String> {
|
||||||
Ok(Message {
|
Ok(Message {
|
||||||
@@ -135,16 +155,6 @@ impl Message {
|
|||||||
MessageBuilder::new(id, author)
|
MessageBuilder::new(id, author)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts the message into an Rc<RefCell<Message>>
|
|
||||||
pub fn into_rc(self) -> Rc<RefCell<Self>> {
|
|
||||||
Rc::new(RefCell::new(self))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Builds a message from a builder and wraps it in Rc<RefCell>
|
|
||||||
pub fn build_rc(builder: MessageBuilder) -> Result<Rc<RefCell<Self>>, String> {
|
|
||||||
builder.build().map(|m| Rc::new(RefCell::new(m)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a human-readable string representing how long ago the message was created
|
/// Returns a human-readable string representing how long ago the message was created
|
||||||
pub fn ago(&self) -> SharedString {
|
pub fn ago(&self) -> SharedString {
|
||||||
let input_time = match Local.timestamp_opt(self.created_at.as_u64() as i64, 0) {
|
let input_time = match Local.timestamp_opt(self.created_at.as_u64() as i64, 0) {
|
||||||
|
|||||||
Reference in New Issue
Block a user