diff --git a/src/message_log.rs b/src/message_log.rs index cea0d59..2cf3e21 100644 --- a/src/message_log.rs +++ b/src/message_log.rs @@ -7,7 +7,7 @@ use chrono::{DateTime, Local}; use iced::{ alignment::Horizontal, widget::{column, container, scrollable, text, Container}, - Background, Color, Length, + Background, Color, Element, Length, }; use irc::proto::BatchSubCommand; use std::collections::{HashMap, HashSet}; @@ -297,20 +297,42 @@ impl<'a> MessageLog { let channel = self.get_mut(Some(channel_name)); if channel.is_multiline { - channel.multiline_nickname = Some(nickname.into()); - channel - .multiline_privmsgs - .as_mut() - .unwrap() - .push(message.into()); - - if let Some(message_id) = message_id { - channel.multiline_message_id = Some(message_id.into()); - } - + Self::handle_multiline_message(channel, nickname, message, message_id); return; } + Self::handle_singleline_message(channel, nickname, message, message_id, timestamp); + + if !is_active { + Self::update_unread_counts(current_nickname, channel, message); + } + } + + fn handle_multiline_message( + channel: &mut Channel, + nickname: &str, + message: &str, + message_id: Option<&str>, + ) { + channel.multiline_nickname = Some(nickname.into()); + channel + .multiline_privmsgs + .as_mut() + .unwrap() + .push(message.into()); + + if let Some(message_id) = message_id { + channel.multiline_message_id = Some(message_id.into()); + } + } + + fn handle_singleline_message( + channel: &mut Channel, + nickname: &str, + message: &str, + message_id: Option<&str>, + timestamp: &DateTime, + ) { if message_id.is_none() || channel.message_ids.insert(message_id.unwrap().into()) { channel.messages.push(IrcMessage { detail: MessageDetail::Privmsg { @@ -321,13 +343,13 @@ impl<'a> MessageLog { timestamp: *timestamp, }); } + } - if !is_active { - if util::contains_word(current_nickname, message) { - channel.unread_highlights += 1; - } else { - channel.unread_messages += 1; - } + fn update_unread_counts(current_nickname: &str, channel: &mut Channel, message: &str) { + if util::contains_word(current_nickname, message) { + channel.unread_highlights += 1; + } else { + channel.unread_messages += 1; } } @@ -351,28 +373,6 @@ impl<'a> MessageLog { pub fn view(&self, current_nickname: &str) -> Container<'_, crate::ui_message::UiMessage> { let lighter_grey = Color::new(0.93, 0.94, 0.95, 1.0); - let dark_grey = Color::new(0.58, 0.65, 0.65, 1.0); - let lighter_green = Color::new(0.94, 0.99, 0.87, 1.0); - let dark_red = Color::new(0.75, 0.22, 0.17, 1.0); - - let event_appearance = container::Appearance { - background: Some(Background::Color(dark_grey)), - border_radius: 8.0.into(), - text_color: Some(Color::WHITE), - ..Default::default() - }; - - let message_appearance = container::Appearance { - background: Some(Background::Color(Color::WHITE)), - border_radius: 8.0.into(), - ..Default::default() - }; - - let own_message_appearance = container::Appearance { - background: Some(Background::Color(lighter_green)), - border_radius: 8.0.into(), - ..Default::default() - }; let channel = self.get(&self.active_channel).unwrap(); @@ -386,118 +386,13 @@ impl<'a> MessageLog { "{} members - {}", channel.names.len(), channel.topic.as_deref().unwrap_or_default() - )) + )), ]); let messages = channel .messages .iter() - .flat_map(|irc_message| -> Option> { - let timestamp = irc_message.timestamp.format("%H:%M:%S"); - - match &irc_message.detail { - MessageDetail::Join { nickname } => Some( - container( - container( - text(format!("{nickname} joined the channel")) - .horizontal_alignment(Horizontal::Center), - ) - .style(move |_: &_| event_appearance) - .padding([3, 10]), - ) - .width(Length::Fill) - .center_x() - .padding([3, 0]) - .into(), - ), - MessageDetail::Part { nickname, reason } => { - let reason = match reason { - Some(reason) => format!(" ({reason})"), - None => String::new(), - }; - Some( - container( - container(text(format!("{nickname} left the channel{reason}"))) - .style(move |_: &_| event_appearance) - .padding([3, 10]), - ) - .width(Length::Fill) - .center_x() - .padding([3, 0]) - .into(), - ) - } - MessageDetail::Nick { old, new } => Some( - container( - container(text(format!("{old} changed their nickname to {new}"))) - .style(move |_: &_| event_appearance) - .padding([3, 10]), - ) - .width(Length::Fill) - .center_x() - .padding([3, 0]) - .into(), - ), - MessageDetail::Quit { nickname, reason } => { - let reason = match reason { - Some(reason) => format!(" ({reason})"), - None => String::new(), - }; - - Some( - container( - container(text(format!("{nickname} quit the server{reason}"))) - .style(move |_: &_| event_appearance) - .padding([3, 10]), - ) - .width(Length::Fill) - .center_x() - .padding([3, 0]) - .into(), - ) - } - MessageDetail::Privmsg { nickname, message } => { - let is_self = nickname == current_nickname; - - let mut elements = Vec::new(); - if !is_self { - elements.push(text(nickname).style(dark_red).into()) - } - elements.push(text(message).into()); - elements.push( - text(timestamp) - .style(dark_grey) - .horizontal_alignment(Horizontal::Right) - .into(), - ); - - let appearance = if is_self { - own_message_appearance - } else { - message_appearance - }; - - let alignment = if is_self { - Horizontal::Right - } else { - Horizontal::Left - }; - - Some( - container( - container(column(elements)) - .style(move |_: &_| appearance) - .padding([4, 10]), - ) - .width(Length::Fill) - .align_x(alignment) - .padding([4, 8]) - .into(), - ) - } - MessageDetail::Other { message } => Some(text(message).into()), - } - }) + .flat_map(|irc_message| Self::map_irc_message(current_nickname, irc_message)) .collect::>(); container(column![ @@ -509,15 +404,115 @@ impl<'a> MessageLog { ) .height(Length::Fill) .width(Length::Fill) - .style(move |_: &_| container::Appearance { - background: Some(Background::Color(lighter_grey)), - ..Default::default() - }) + .style(move |_: &_| Self::container_appearance(lighter_grey, 0.0)), ]) .height(Length::Fill) .width(Length::Fill) } + fn container_appearance(background_color: Color, border_radius: f32) -> container::Appearance { + container::Appearance { + background: Some(Background::Color(background_color)), + border_radius: border_radius.into(), + ..Default::default() + } + } + + fn map_irc_message( + current_nickname: &str, + irc_message: &IrcMessage, + ) -> Option> { + let dark_grey = Color::new(0.58, 0.65, 0.65, 1.0); + let dark_red = Color::new(0.75, 0.22, 0.17, 1.0); + let lighter_green = Color::new(0.94, 0.99, 0.87, 1.0); + + let timestamp = irc_message.timestamp.format("%H:%M:%S"); + + match &irc_message.detail { + MessageDetail::Join { nickname } => Some(Self::event_container(format!( + "{nickname} joined the channel" + ))), + MessageDetail::Part { nickname, reason } => { + let reason_text = reason + .as_ref() + .map(|r| format!(" ({})", r)) + .unwrap_or_default(); + Some(Self::event_container(format!( + "{nickname} left the channel{reason_text}" + ))) + } + MessageDetail::Nick { old, new } => Some(Self::event_container(format!( + "{old} changed their nickname to {new}" + ))), + MessageDetail::Quit { nickname, reason } => { + let reason_text = reason + .as_ref() + .map(|r| format!(" ({})", r)) + .unwrap_or_default(); + Some(Self::event_container(format!( + "{nickname} quit the server{reason_text}" + ))) + } + MessageDetail::Privmsg { nickname, message } => { + let is_self = nickname == current_nickname; + let text_color = if is_self { dark_red } else { Color::BLACK }; + let message_appearance = Self::container_appearance(Color::WHITE, 8.0); + let own_message_appearance = Self::container_appearance(lighter_green, 8.0); + let mut elements: Vec> = + vec![text(message).style(text_color).into()]; + + if !is_self { + elements.insert(0, text(nickname).style(dark_red).into()); + } + + elements.push( + text(timestamp) + .style(dark_grey) + .horizontal_alignment(Horizontal::Right) + .into(), + ); + + let appearance = if is_self { + own_message_appearance + } else { + message_appearance + }; + + let alignment = if is_self { + Horizontal::Right + } else { + Horizontal::Left + }; + + Some( + container( + container(column(elements)) + .style(move |_: &_| appearance) + .padding([4, 10]), + ) + .width(Length::Fill) + .align_x(alignment) + .padding([4, 8]) + .into(), + ) + } + MessageDetail::Other { message } => Some(text(message).into()), + } + } + + fn event_container(content: String) -> iced::Element<'a, crate::ui_message::UiMessage> { + let dark_grey = Color::new(0.58, 0.65, 0.65, 1.0); + container( + container(text(content).style(Color::WHITE).horizontal_alignment(Horizontal::Center)) + .style(move |_: &_| Self::container_appearance(dark_grey, 8.0)) + .padding([3, 10]), + ) + .width(Length::Fill) + .center_x() + .padding([3, 0]) + .into() + } + pub fn on_topic(&mut self, channel: &str, topic: &str) { self.get_mut(Some(channel)).topic = Some(topic.into()); }