From 5b94e67f0dad164dffb2cf0aed5c373f56dd4a84 Mon Sep 17 00:00:00 2001 From: Sijmen Date: Wed, 29 Nov 2023 01:07:31 +0100 Subject: [PATCH] Fix events in chat history being in the wrong order or missing --- src/cri.rs | 21 +++---- src/message_log.rs | 140 +++++++++++++++++++++++++++++++++++---------- 2 files changed, 122 insertions(+), 39 deletions(-) diff --git a/src/cri.rs b/src/cri.rs index a1744c4..dbcef81 100644 --- a/src/cri.rs +++ b/src/cri.rs @@ -115,12 +115,11 @@ impl Cri { .borrow() .send(IrcCommand::JOIN(channel.clone(), tokens.next().map(String::from), None).into()) .unwrap(); - self.message_log.set_active(Some(channel)); + self.message_log.set_active(Some(&channel)); } fn handle_query_command(&mut self, mut tokens: std::str::SplitWhitespace<'_>) { - self.message_log - .set_active(Some(tokens.next().unwrap().into())); + self.message_log.set_active(Some(tokens.next().unwrap())); } fn handle_list_command(&self) { @@ -137,11 +136,7 @@ impl Cri { timestamp: DateTime, message_id: Option<&str>, ) { - let already_joined = self.message_log.has_channel(dbg!(chanlist)); - self.message_log - .on_join(chanlist, source_nickname, ×tamp, message_id); - - if !already_joined + if !self.message_log.has_channel(chanlist) && source_nickname == &self.nickname && self.capabilities.contains(&"draft/chathistory".into()) { @@ -160,6 +155,9 @@ impl Cri { .into(), ) .unwrap(); + } else { + self.message_log + .on_join(chanlist, source_nickname, ×tamp, message_id); } } } @@ -214,6 +212,8 @@ impl Application for Cri { let message_id = tags_map.get("msgid").cloned().flatten(); let message_id = message_id.as_deref(); + let batch = tags_map.get("batch").cloned().flatten(); + match &message.command { IrcCommand::CAP(_, CapSubCommand::ACK, capability, _) => { let capability = capability.as_ref().unwrap(); @@ -245,6 +245,7 @@ impl Application for Cri { comment.as_deref(), ×tamp, message_id, + batch, ); } @@ -300,8 +301,8 @@ impl Application for Cri { self.input_value.clear(); } - ui_message::UiMessage::HandleChannelPress(channel) => { - self.message_log.set_active(channel) + ui_message::UiMessage::HandleChannelPress(channel_name) => { + self.message_log.set_active(channel_name.as_deref()) } ui_message::UiMessage::None => (), } diff --git a/src/message_log.rs b/src/message_log.rs index cf7cda0..707cfe7 100644 --- a/src/message_log.rs +++ b/src/message_log.rs @@ -10,13 +10,14 @@ use irc::proto::BatchSubCommand; use regex::Regex; use std::collections::{HashMap, HashSet}; -#[derive(Default)] pub struct Channel { + pub channel_name: Option, + pub messages: Vec, pub message_ids: HashSet, pub names: Vec, - pub topic: Option, + pub unread_messages: i32, pub unread_highlights: i32, pub unread_events: i32, @@ -28,6 +29,29 @@ pub struct Channel { pub multiline_message_id: Option, } +impl Channel { + fn new(channel_name: Option<&str>) -> Self { + Self { + channel_name: channel_name.map(String::from), + + messages: Vec::new(), + message_ids: HashSet::new(), + names: Vec::new(), + topic: None, + + unread_messages: 0, + unread_highlights: 0, + unread_events: 0, + + is_multiline: false, + multiline_privmsgs: None, + multiline_timestamp: None, + multiline_nickname: None, + multiline_message_id: None, + } + } +} + pub struct MessageLog { channels: HashMap, Channel>, pub active_channel: Option, @@ -39,7 +63,7 @@ pub struct MessageLog { impl<'a> MessageLog { pub fn new() -> Self { let mut channels = HashMap::new(); - channels.insert(None, Default::default()); + channels.insert(None, Channel::new(None)); Self { channels, active_channel: None, @@ -61,8 +85,10 @@ impl<'a> MessageLog { self.channels.get(channel) } - pub fn get_mut(&mut self, channel_name: Option) -> &mut Channel { - self.channels.entry(channel_name).or_default() + pub fn get_mut(&mut self, channel_name: Option<&str>) -> &mut Channel { + self.channels + .entry(channel_name.map(String::from)) + .or_insert_with(|| Channel::new(channel_name)) } pub fn on_join( @@ -73,7 +99,7 @@ impl<'a> MessageLog { message_id: Option<&str>, ) { let is_active = self.active_channel.as_deref() != Some(channel_name); - let channel = self.get_mut(Some(channel_name.into())); + let channel = self.get_mut(Some(channel_name)); if message_id.is_some() && !channel.message_ids.insert(message_id.unwrap().into()) { return; @@ -101,7 +127,7 @@ impl<'a> MessageLog { message_id: Option<&str>, ) { let is_active = self.active_channel.as_deref() != Some(channel_name); - let channel = self.get_mut(Some(channel_name.into())); + let channel = self.get_mut(Some(channel_name)); if message_id.is_some() && !channel.message_ids.insert(message_id.unwrap().into()) { return; @@ -152,22 +178,49 @@ impl<'a> MessageLog { reason: Option<&str>, timestamp: &DateTime, message_id: Option<&str>, + batch: Option, ) { - // TODO increment event counter for each relevant channel - for channel in self.channels.values_mut() { + if let Some(batch) = batch { + let (subcommand, channel_name) = &self.batch_channels[&batch]; + if subcommand == &BatchSubCommand::CUSTOM("CHATHISTORY".into()) { + if let Some(channel) = self.channels.get_mut(&Some(channel_name.to_owned())) { + if message_id.is_some() + && !channel.message_ids.insert(message_id.unwrap().into()) + { + return; + } + + Self::add_quit( + channel, + self.active_channel.as_deref(), + nickname, + reason, + message_id, + timestamp, + ); + + return; + } + } + } + + for (_, channel) in self.channels.iter_mut() { + if !channel.names.is_empty() && !channel.names.contains(&nickname.into()) { + continue; + } + if message_id.is_some() && !channel.message_ids.insert(message_id.unwrap().into()) { continue; } - // TODO only show in relevant channels - channel.messages.push(IrcMessage { - detail: MessageDetail::Quit { - nickname: nickname.into(), - reason: reason.map(String::from), - }, - message_id: message_id.map(String::from), - timestamp: *timestamp, - }) + Self::add_quit( + channel, + self.active_channel.as_deref(), + nickname, + reason, + message_id, + timestamp, + ); } } @@ -187,8 +240,7 @@ impl<'a> MessageLog { (subcommand.unwrap().clone(), channel_name.clone()), ); - let channel = self.get_mut(Some(channel_name.clone())); - + let channel = self.get_mut(Some(channel_name)); channel.is_multiline = true; channel.multiline_privmsgs = Some(Vec::new()); channel.multiline_timestamp = Some(*timestamp); @@ -198,10 +250,15 @@ impl<'a> MessageLog { tag.into(), (subcommand.unwrap().clone(), channel_name.clone()), ); + + // TODO Collect messages into a separate list and update them + // at the end of the batch instead of just clearing the channel + let channel = self.get_mut(Some(channel_name)); + channel.messages.clear(); } } else if let Some(tag) = tag.strip_prefix('-') { if let Some((subcommand, channel_name)) = self.batch_channels.remove(tag) { - let channel = self.get_mut(Some(channel_name.clone())); + let channel = self.get_mut(Some(&channel_name)); if subcommand == BatchSubCommand::CUSTOM("DRAFT/MULTILINE".into()) { channel.is_multiline = false; @@ -219,7 +276,7 @@ impl<'a> MessageLog { ×tamp, ); } else if subcommand == BatchSubCommand::CUSTOM("CHATHISTORY".into()) { - channel.messages.sort_by_key(|m| m.timestamp); + // TODO } } } @@ -235,7 +292,7 @@ impl<'a> MessageLog { timestamp: &DateTime, ) { let is_active = self.active_channel.as_deref() == Some(channel_name); - let channel = self.get_mut(Some(channel_name.into())); + let channel = self.get_mut(Some(channel_name)); if channel.is_multiline { channel.multiline_nickname = Some(nickname.into()); @@ -284,8 +341,8 @@ impl<'a> MessageLog { }) } - pub fn set_active(&mut self, channel_name: Option) { - self.active_channel = channel_name.clone(); + pub fn set_active(&mut self, channel_name: Option<&str>) { + self.active_channel = channel_name.map(String::from); let channel = self.get_mut(channel_name); channel.unread_events = 0; channel.unread_messages = 0; @@ -462,12 +519,37 @@ impl<'a> MessageLog { } pub fn on_topic(&mut self, channel: &str, topic: &str) { - self.get_mut(Some(channel.into())).topic = Some(topic.into()); + self.get_mut(Some(channel)).topic = Some(topic.into()); } pub fn on_names_reply(&mut self, channel: &str, names: Vec<&str>) { - self.get_mut(Some(channel.into())) - .names - .extend(names.iter().map(|&n| String::from(n))); + self.get_mut(Some(channel)).names.extend( + names + .iter() + .map(|&n| String::from(n.trim_matches(&['~', '&', '@', '%', '+'] as &[_]))), + ); + } + + fn add_quit( + channel: &mut Channel, + active_channel_name: Option<&str>, + nickname: &str, + reason: Option<&str>, + message_id: Option<&str>, + timestamp: &DateTime, + ) { + let is_active = active_channel_name != Some(channel.channel_name.as_ref().unwrap()); + if is_active { + channel.unread_events += 1; + } + + channel.messages.push(IrcMessage { + detail: MessageDetail::Quit { + nickname: nickname.into(), + reason: reason.map(String::from), + }, + message_id: message_id.map(String::from), + timestamp: *timestamp, + }); } }