aboutsummaryrefslogblamecommitdiffstats
path: root/src/message_view.rs
blob: 4709dd6f4c531a2fa3fe6d0150aa0a48b1474635 (plain) (tree)








































































































































































                                                                                           
use std::borrow::Cow;

use chrono::NaiveDate;
use iced::{
    alignment::Horizontal::{self, Right},
    border::Radius,
    color,
    theme::Palette,
    widget::{button, column, container, row, scrollable, text, text_input, Column},
    Border, Color, Element,
    Length::{Fill, Shrink},
    Theme,
};
use indexmap::IndexMap;
use jid::JID;
use luz::chat::Message as ChatMessage;
use uuid::Uuid;

pub struct MessageView {
    pub jid: JID,
    pub message_history: IndexMap<Uuid, ChatMessage>,
    pub new_message: String,
}

#[derive(Debug, Clone)]
pub enum Message {
    MessageHistory(Vec<ChatMessage>),
    Message(ChatMessage),
    MessageCompose(String),
    SendMessage(String),
}

pub enum Action {
    None,
    SendMessage(String),
}

impl MessageView {
    pub fn new(jid: JID) -> Self {
        Self {
            jid,
            // TODO: save position in message history
            message_history: IndexMap::new(),
            // TODO: save draft (as part of chat struct?)
            new_message: String::new(),
        }
    }
    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(s) => {
                self.new_message = s;
                Action::None
            }
            Message::SendMessage(m) => {
                self.new_message = String::new();
                Action::SendMessage(m)
            }
        }
    }
    pub fn view(&self) -> Element<Message> {
        let mut messages_view = column![].spacing(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));
            }
            messages_view = messages_view.push(self.message(message));
        }
        let message_send_input = row![
            text_input("new message", &self.new_message).on_input(Message::MessageCompose),
            button("send").on_press(Message::SendMessage(self.new_message.clone()))
        ];
        column![
            scrollable(messages_view)
                .height(Fill)
                .width(Fill)
                .spacing(1)
                .anchor_bottom(),
            message_send_input
        ]
        .into()
    }

    pub fn message<'a>(&'a self, message: &'a ChatMessage) -> 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()),
                        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: 4.,
                            radius: Radius::new(16),
                        })
                }),
            )
            .align_left(Fill)
            .into()
        } else {
            let element: Element<Message> = container(
                container(
                    column![
                        text(message.body.body.as_str()),
                        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: 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()
}