diff options
Diffstat (limited to 'src/message_view.rs')
-rw-r--r-- | src/message_view.rs | 267 |
1 files changed, 161 insertions, 106 deletions
diff --git a/src/message_view.rs b/src/message_view.rs index 16e8ac1..f5319bd 100644 --- a/src/message_view.rs +++ b/src/message_view.rs @@ -1,29 +1,30 @@ -use std::borrow::Cow; +use std::{path::PathBuf, time::Duration}; -use chrono::NaiveDate; +use chrono::{NaiveDate, NaiveDateTime, TimeDelta}; +use iced::color; +use iced::widget::text_editor; use iced::{ - alignment::Horizontal::{self, Right}, border::Radius, - color, - theme::Palette, - widget::{ - button, column, container, horizontal_space, row, scrollable, text, text_editor, - text_editor::Content, text_input, Column, - }, - Border, Color, Element, + font::{Style, Weight}, + widget::{button, column, container, image, row, scrollable, text, text_editor::Content}, + Border, Color, Element, Font, Length::{Fill, Shrink}, Theme, }; use indexmap::IndexMap; use jid::JID; -use luz::chat::Message as ChatMessage; use serde::{Deserialize, Serialize}; use uuid::Uuid; +use crate::{icons::Icon, MacawChat, MacawMessage}; + pub struct MessageView { + pub file_root: PathBuf, + // references chats, users + pub chat: MacawChat, + // references users, messages + pub messages: IndexMap<Uuid, MacawMessage>, pub config: Config, - pub jid: JID, - pub message_history: IndexMap<Uuid, ChatMessage>, pub new_message: Content, pub shift_pressed: bool, } @@ -43,8 +44,8 @@ impl Default for Config { #[derive(Debug, Clone)] pub enum Message { - MessageHistory(Vec<ChatMessage>), - Message(ChatMessage), + MessageHistory(Vec<MacawMessage>), + Message(MacawMessage), MessageCompose(text_editor::Action), SendMessage(String), } @@ -55,42 +56,22 @@ pub enum Action { } impl MessageView { - pub fn new(jid: JID, config: &super::Config) -> Self { + pub fn new(chat: MacawChat, config: &super::Config, file_root: PathBuf) -> Self { Self { - jid, + chat, // TODO: save position in message history - message_history: IndexMap::new(), + messages: IndexMap::new(), // TODO: save draft (as part of chat struct?) new_message: Content::new(), config: config.message_view_config.clone(), // TODO: have centralised modifier state location? shift_pressed: false, + file_root, } } pub fn update(&mut self, message: Message) -> Action { match message { - Message::MessageHistory(messages) => { - if self.message_history.is_empty() { - self.message_history = messages - .into_iter() - .map(|message| (message.id.clone(), message)) - .collect(); - } - Action::None - } - Message::Message(message) => { - let i = self - .message_history - .iter() - .position(|(_id, m)| m.timestamp > message.timestamp); - if let Some(i) = i { - self.message_history.insert_before(i, message.id, message); - } else { - self.message_history.insert(message.id, message); - } - Action::None - } Message::MessageCompose(a) => { match &a { text_editor::Action::Edit(edit) => match edit { @@ -120,30 +101,90 @@ impl MessageView { self.new_message = Content::new(); Action::SendMessage(m) } + Message::MessageHistory(macaw_messages) => { + if self.messages.is_empty() { + self.messages = macaw_messages + .into_iter() + .map(|message| (message.id, message)) + .collect() + } else { + for message in macaw_messages { + let index = match self + .messages + .binary_search_by(|_, value| value.timestamp.cmp(&message.timestamp)) + { + Ok(i) => i, + Err(i) => i, + }; + self.messages.insert_before(index, message.id, message); + } + } + Action::None + } + Message::Message(macaw_message) => { + if let Some((_, last)) = self.messages.last() { + if last.timestamp < macaw_message.timestamp { + self.messages.insert(macaw_message.id, macaw_message); + } else { + let index = match self.messages.binary_search_by(|_, value| { + value.timestamp.cmp(&macaw_message.timestamp) + }) { + Ok(i) => i, + Err(i) => i, + }; + self.messages + .insert_before(index, macaw_message.id, macaw_message); + } + } else { + self.messages.insert(macaw_message.id, macaw_message); + } + Action::None + } } } pub fn view(&self) -> Element<Message> { let mut messages_view = column![].spacing(8).padding(8); - let mut latest_date = NaiveDate::MIN; - for (_id, message) in &self.message_history { - let message_date = message.timestamp.naive_local().date(); - if message_date > latest_date { - latest_date = message_date; - messages_view = messages_view.push(date(latest_date)); + let mut last_timestamp = NaiveDateTime::MIN; + let mut last_user: Option<JID> = None; + for (_id, message) in &self.messages { + let message_timestamp = message.timestamp.naive_local(); + if message_timestamp.date() > last_timestamp.date() { + messages_view = messages_view.push(date(message_timestamp.date())); } - messages_view = messages_view.push(self.message(message)); + if last_user.as_ref() != Some(&message.from.jid) + || message_timestamp - last_timestamp > TimeDelta::minutes(3) + { + messages_view = messages_view.push(self.message(message, true)); + } else { + messages_view = messages_view.push(self.message(message, false)); + } + last_user = Some(message.from.jid.clone()); + last_timestamp = message_timestamp; } let text_editor = text_editor(&self.new_message) .placeholder("new message") .on_action(Message::MessageCompose) - .wrapping(text::Wrapping::WordOrGlyph); + .wrapping(text::Wrapping::WordOrGlyph) + .style(|theme, status| text_editor::Style { + background: color!(0xdcdcdc).into(), + border: Border { + color: Color::BLACK, + width: 0.0, + radius: 0.into(), + }, + icon: color!(0x00000000), + placeholder: color!(0xacacac), + value: color!(0x000000), + selection: color!(0xffce07), + }); let message_send_input = row![ text_editor, - button("send").on_press(Message::SendMessage(self.new_message.text())) + // button(Icon::NewBubble24).on_press(Message::SendMessage(self.new_message.text())) ] .padding(8); column![ + self.header(), scrollable(messages_view) .height(Fill) .width(Fill) @@ -154,68 +195,82 @@ impl MessageView { .into() } - pub fn message<'a>(&'a self, message: &'a ChatMessage) -> Element<'a, Message> { + pub fn header(&self) -> Element<'_, Message> { + // TODO: contact stored here for name + let mut bold = Font::with_name("K2D"); + bold.weight = Weight::Bold; + let mut sweet = Font::with_name("Diolce"); + sweet.style = Style::Italic; + let mut name_and_jid = column![]; + if let Some(nick) = &self.chat.user.nick { + name_and_jid = name_and_jid.push(text(nick).font(bold).size(20)); + } + let jid = self.chat.user.jid.as_bare().to_string(); + name_and_jid = name_and_jid.push(text(jid).font(sweet)); + let mut header = row![]; + if let Some(avatar) = &self.chat.user.avatar { + let mut path = self.file_root.join(avatar); + path.set_extension("jpg"); + header = header.push(container(image(path).width(48).height(48))); + } + header = header.push(name_and_jid); + container( + container(header.spacing(8).padding(8)) + .style(|theme: &Theme| { + container::Style::default() + .background(theme.extended_palette().background.strong.color) + }) + .width(Fill), + ) + .padding(8) + .width(Fill) + .into() + } + + pub fn message<'a>(&'a self, message: &'a MacawMessage, major: bool) -> Element<'a, Message> { let timestamp = message.timestamp.naive_local(); let timestamp = timestamp.time().format("%H:%M").to_string(); - if self.jid == message.from.as_bare() { - container( - container( - column![ - text(message.body.body.as_str()).wrapping(text::Wrapping::WordOrGlyph), - container(text(timestamp).wrapping(text::Wrapping::None).size(12)) // .align_right(Fill) - ] - .width(Shrink) - .max_width(500), - ) - .padding(16) - .style(|theme: &Theme| { - let palette = theme.extended_palette(); - container::Style::default() - .background(palette.primary.weak.color) - .border(Border { - color: Color::BLACK, - width: 0., - // width: 4., - radius: Radius::new(16), - }) - }), - ) - .align_left(Fill) - .into() + if major { + let nick: String = if let Some(nick) = &message.from.nick { + nick.to_string() + } else { + message.from.jid.as_bare().to_string() + }; + let mut bold = Font::with_name("K2D"); + bold.weight = Weight::Bold; + let mut header = row![text(nick).font(bold), text(timestamp)].spacing(8); + if let Some(delivery) = message.delivery { + let icon = match delivery { + filamento::chat::Delivery::Sending => Some(Icon::Sending16), + filamento::chat::Delivery::Written => None, + filamento::chat::Delivery::Sent => Some(Icon::Sent16), + filamento::chat::Delivery::Delivered => Some(Icon::Delivered16), + filamento::chat::Delivery::Read => Some(Icon::Delivered16), + filamento::chat::Delivery::Failed => Some(Icon::Error16Color), + filamento::chat::Delivery::Queued => Some(Icon::Sending16), + }; + if let Some(icon) = icon { + header = header.push(icon); + } + } + let message_right = column![header, text(&message.body.body)].spacing(8); + let mut major_message = row([]); + if let Some(avatar) = &message.from.avatar { + let mut path = self.file_root.join(avatar); + path.set_extension("jpg"); + // info!("got avatar: {:?}", path); + major_message = major_message.push(container(image(path).width(48).height(48))); + } + major_message = major_message.push(message_right); + major_message.spacing(8).into() } else { - let element: Element<Message> = container( - container( - column![ - text(message.body.body.as_str()).wrapping(text::Wrapping::WordOrGlyph), - container(text(timestamp).wrapping(text::Wrapping::None).size(12)) - .align_right(Fill) // row![ - // // horizontal_space(), - // // horizontal_space(), - // text(timestamp).wrapping(text::Wrapping::None).size(12) - // ] // container(text(timestamp).wrapping(text::Wrapping::None).size(12)) - // .align_right(Fill) - ] - .width(Shrink) - .max_width(500), - ) - .padding(16) - .style(|theme: &Theme| { - let palette = theme.extended_palette(); - container::Style::default() - .background(palette.primary.base.color) - .border(Border { - color: Color::BLACK, - width: 0., - // width: 4., - radius: Radius::new(16), - }) - }), - ) - .align_right(Fill) - .into(); - // element.explain(Color::BLACK) - element + row![ + container(text(timestamp)).width(48), + text(&message.body.body) + ] + .spacing(8) + .into() } } } |