use std::{ borrow::{Borrow, Cow}, cell::{Ref, RefCell}, ops::Deref, rc::Rc, }; use chrono::NaiveDate; use filamento::chat::Message as ChatMessage; 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, Length::{Fill, Shrink}, Theme, }; use indexmap::IndexMap; use jid::JID; use serde::{Deserialize, Serialize}; use uuid::Uuid; use crate::{MacawChat, MacawMessage}; pub struct MessageView { pub config: Config, pub chat: Rc>, pub messages: Vec>>, pub new_message: Content, pub shift_pressed: bool, } impl MessageView { pub fn chat(&self) -> Ref<'_, MacawChat> { self.chat.as_ref().borrow() } pub fn messages( &self, ) -> impl Iterator + use<'_>> + use<'_> { self.messages .iter() .map(|message| message.as_ref().borrow()) } } #[derive(Serialize, Deserialize, Clone)] pub struct Config { pub send_on_enter: bool, } impl Default for Config { fn default() -> Self { Self { send_on_enter: true, } } } #[derive(Debug, Clone)] pub enum Message { // MessageHistory(Vec>>), // Message(Rc>), MessageCompose(text_editor::Action), SendMessage(String), } pub enum Action { None, SendMessage(String), } impl MessageView { pub fn new(chat: Rc>, config: &super::Config) -> Self { Self { chat, // TODO: save position in message history messages: Vec::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, } } pub fn update(&mut self, message: Message) -> Action { match message { Message::MessageCompose(a) => { match &a { text_editor::Action::Edit(edit) => match edit { text_editor::Edit::Enter => { if self.config.send_on_enter { if !self.shift_pressed { let message = self.new_message.text(); self.new_message = Content::new(); return Action::SendMessage(message); } } else { if self.shift_pressed { let message = self.new_message.text(); self.new_message = Content::new(); return Action::SendMessage(message); } } } _ => {} }, _ => {} } self.new_message.perform(a); Action::None } Message::SendMessage(m) => { self.new_message = Content::new(); Action::SendMessage(m) } } } pub fn view(&self) -> Element { let mut messages_view = column![].spacing(8).padding(8); let mut latest_date = NaiveDate::MIN; for message in self.messages() { 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)); } messages_view = messages_view.push(self.message(message)); } let text_editor = text_editor(&self.new_message) .placeholder("new message") .on_action(Message::MessageCompose) .wrapping(text::Wrapping::WordOrGlyph); let message_send_input = row![ text_editor, button("send").on_press(Message::SendMessage(self.new_message.text())) ] .padding(8); column![ scrollable(messages_view) .height(Fill) .width(Fill) .spacing(1) .anchor_bottom(), message_send_input ] .into() } pub fn message<'a, 'b, M>(&'a self, message: M) -> Element<'b, Message> where M: Deref + 'b, { let timestamp = message.timestamp.naive_local(); let timestamp = timestamp.time().format("%H:%M").to_string(); if self.chat().user().jid == message.user().jid { container( container( column![ text(message.body.body.clone()).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() } else { let element: Element = container( container( column![ text(message.body.body.clone()).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 } } } pub fn date(date: NaiveDate) -> Element<'static, Message> { container(text(date.to_string())).center_x(Fill).into() }