Skip to content

Commit 8a465b5

Browse files
committed
Message Grouping
1 parent b9d0f9c commit 8a465b5

File tree

5 files changed

+108
-28
lines changed

5 files changed

+108
-28
lines changed

src/chat/src/message.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ pub trait Message: Clone {
77
fn get_nonce(&self) -> Option<&String>;
88
}
99

10-
pub trait MessageAuthor {
10+
pub trait MessageAuthor: PartialEq + Eq {
1111
fn get_display_name(&self) -> impl Element;
1212
fn get_icon(&self) -> String;
13+
fn get_id(&self) -> String;
1314
}

src/discord/src/client.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ impl RawEventHandler for RawClient {
8787
user.get("id").unwrap().as_str().unwrap(),
8888
user.get("avatar").unwrap().as_str().unwrap()
8989
),
90+
id: user.get("id").unwrap().as_str().unwrap().to_owned(),
9091
});
9192
}
9293
}
@@ -107,6 +108,7 @@ impl EventHandler for DiscordClient {
107108
author: DiscordMessageAuthor {
108109
display_name: DisplayName(msg.author.name.clone()),
109110
icon: msg.author.avatar_url().unwrap_or(msg.author.default_avatar_url()),
111+
id: msg.author.id.to_string(),
110112
},
111113
content: DiscordMessageContent {
112114
content: msg.content.clone(),

src/discord/src/message/author.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,16 @@ use scope_chat::message::MessageAuthor;
55
pub struct DiscordMessageAuthor {
66
pub display_name: DisplayName,
77
pub icon: String,
8+
pub id: String,
89
}
910

11+
impl PartialEq for DiscordMessageAuthor {
12+
fn eq(&self, other: &Self) -> bool {
13+
self.id == other.id
14+
}
15+
}
16+
impl Eq for DiscordMessageAuthor {}
17+
1018
impl MessageAuthor for DiscordMessageAuthor {
1119
fn get_display_name(&self) -> impl Element {
1220
self.display_name.clone().into_element()
@@ -15,6 +23,10 @@ impl MessageAuthor for DiscordMessageAuthor {
1523
fn get_icon(&self) -> String {
1624
self.icon.clone()
1725
}
26+
27+
fn get_id(&self) -> String {
28+
self.id.clone()
29+
}
1830
}
1931

2032
#[derive(Clone, IntoElement)]

src/ui/src/channel/message.rs

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,63 @@
1-
use gpui::{div, img, rgb, IntoElement, ParentElement, Styled};
1+
use gpui::{div, img, rgb, Element, IntoElement, ParentElement, Styled};
22
use scope_chat::message::{Message, MessageAuthor};
33

4-
pub fn message(message: impl Message) -> impl IntoElement {
4+
#[derive(Clone)]
5+
pub struct MessageGroup<M: Message> {
6+
contents: Vec<M>,
7+
}
8+
9+
impl<M: Message> MessageGroup<M> {
10+
pub fn new(message: M) -> MessageGroup<M> {
11+
MessageGroup { contents: vec![message] }
12+
}
13+
14+
pub fn get_author<'s>(&'s self) -> &'s (impl MessageAuthor + 's) {
15+
self.contents.get(0).unwrap().get_author()
16+
}
17+
18+
pub fn add(&mut self, message: M) {
19+
// FIXME: This is scuffed, should be using PartialEq trait.
20+
if self.get_author().get_id() != message.get_author().get_id() {
21+
panic!("Authors must match in a message group")
22+
}
23+
24+
self.contents.push(message);
25+
}
26+
27+
pub fn contents<'s>(&'s self) -> impl IntoIterator<Item = impl Element + 's> {
28+
self.contents.iter().map(|v| v.get_content())
29+
}
30+
31+
pub fn find_matching(&self, nonce: &String) -> Option<usize> {
32+
for haystack in self.contents.iter().zip(0usize..) {
33+
if haystack.0.get_nonce().is_some() && haystack.0.get_nonce().unwrap() == nonce {
34+
return Some(haystack.1);
35+
}
36+
}
37+
38+
return None;
39+
}
40+
41+
pub fn size(&self) -> usize {
42+
self.contents.len()
43+
}
44+
45+
pub fn remove(&mut self, index: usize) {
46+
if self.size() == 1 {
47+
panic!("Cannot remove such that it would leave the group empty.");
48+
}
49+
50+
self.contents.remove(index);
51+
}
52+
}
53+
54+
pub fn message<M: Message>(message: MessageGroup<M>) -> impl IntoElement {
555
div()
656
.flex()
757
.flex_row()
858
.text_color(rgb(0xFFFFFF))
959
.gap_2()
1060
.p_2()
1161
.child(img(message.get_author().get_icon()).object_fit(gpui::ObjectFit::Fill).bg(rgb(0xFFFFFF)).rounded_full().w_12().h_12())
12-
.child(div().flex().flex_col().child(message.get_author().get_display_name()).child(message.get_content()))
62+
.child(div().flex().flex_col().child(message.get_author().get_display_name()).child(div().children(message.contents())))
1363
}

src/ui/src/channel/message_list.rs

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,65 @@
11
use gpui::{div, IntoElement, ListAlignment, ListState, ParentElement, Pixels};
22

3-
use scope_chat::message::Message;
3+
use scope_chat::message::{Message, MessageAuthor};
44

5-
use super::message::message;
5+
use super::message::{message, MessageGroup};
66

77
#[derive(Clone)]
88
pub struct MessageList<M: Message + 'static> {
9-
real_messages: Vec<M>,
10-
pending_messages: Vec<M>,
9+
messages: Vec<MessageGroup<M>>,
1110
}
1211

1312
impl<M: Message> MessageList<M> {
1413
pub fn new() -> MessageList<M> {
15-
Self {
16-
real_messages: Vec::default(),
17-
pending_messages: Vec::default(),
18-
}
14+
Self { messages: Vec::default() }
1915
}
2016

2117
pub fn add_external_message(&mut self, message: M) {
22-
if let Some((_, pending_index)) = self
23-
.pending_messages
24-
.iter()
25-
.zip(0..)
26-
.find(|(msg, _)| msg.get_nonce().map(|v1| message.get_nonce().map(|v2| v2 == v1).unwrap_or(false)).unwrap_or(false))
27-
{
28-
self.pending_messages.remove(pending_index);
18+
if let Some(nonce) = message.get_nonce() {
19+
let mut removal_index: Option<usize> = None;
20+
21+
for (group, index) in self.messages.iter_mut().zip(0..) {
22+
let matching = group.find_matching(nonce);
23+
24+
if let Some(matching) = matching {
25+
if group.size() == 1 {
26+
removal_index = Some(index);
27+
} else {
28+
group.remove(matching);
29+
}
30+
}
31+
}
32+
33+
if let Some(removal_index) = removal_index {
34+
self.messages.remove(removal_index);
35+
}
2936
}
3037

31-
self.real_messages.push(message);
38+
let last = self.messages.last_mut();
39+
40+
if last.is_some() && last.as_ref().unwrap().get_author().get_id() == message.get_author().get_id() {
41+
last.unwrap().add(message);
42+
} else {
43+
self.messages.push(MessageGroup::new(message));
44+
}
3245
}
3346

3447
pub fn add_pending_message(&mut self, pending_message: M) {
35-
self.pending_messages.push(pending_message);
48+
let last = self.messages.last_mut();
49+
50+
if last.is_some() && last.as_ref().unwrap().get_author().get_id() == pending_message.get_author().get_id() {
51+
last.unwrap().add(pending_message);
52+
} else {
53+
self.messages.push(MessageGroup::new(pending_message));
54+
}
3655
}
3756

3857
pub fn length(&self) -> usize {
39-
self.real_messages.len() + self.pending_messages.len()
58+
self.messages.len()
4059
}
4160

42-
pub fn get(&self, index: usize) -> Option<&M> {
43-
if index >= self.real_messages.len() {
44-
self.pending_messages.get(index - self.real_messages.len())
45-
} else {
46-
self.real_messages.get(index)
47-
}
61+
pub fn get(&self, index: usize) -> Option<&MessageGroup<M>> {
62+
self.messages.get(index)
4863
}
4964

5065
pub fn create_list_state(&self) -> ListState {

0 commit comments

Comments
 (0)