mayday 0.1.0

A multi-messaging-sevice aggregator into an all-in-one application (android's app beeper-like)
Documentation
use crate::app::App;
use crate::states::AppState;
use chrono::Local;
use ratatui::layout::Margin;
use ratatui::prelude::{Alignment, Constraint, Layout, Line, Rect, Style, Stylize};
use ratatui::widgets::{Block, Borders, Paragraph, Scrollbar, ScrollbarOrientation};
use ratatui::Frame;
use textwrap::wrap;

impl App<'_> {
    pub fn render_discussion_layout(&mut self, frame: &mut Frame, area: Rect) {
        let [_, messages_area, text_area_area, _] = Layout::vertical(vec![
            Constraint::Length(1),
            Constraint::Fill(1),
            Constraint::Length(4),
            Constraint::Length(1)
        ])
            .areas(area);

        let inner_messages_area = messages_area.inner(Margin::new(2, 0));
        let inner_separator_area = text_area_area.inner(Margin::new(1, 0));
        let inner_text_area_area = text_area_area.inner(Margin::new(1, 1));

        let (messaging_service, _) = self.get_selected_messaging_services();

        let mut block = Block::bordered()
            .title_top(messaging_service.discussion_name.clone())
            .title_alignment(Alignment::Center);

        if self.state == AppState::Main {
            block = block.dark_gray();
        }

        frame.render_widget(block, area);

        let mut messages = vec![];
        let mut last_sender = &None;

        for message in &messaging_service.tmp_messages {
            let mut alignment = Alignment::Right;

            let max_length = self.get_max_line_length(&message.text);
            let lines = wrap(&message.text, max_length);

            if let Some(sender) = &message.sender {
                alignment = Alignment::Left;

                if last_sender != &message.sender {
                    messages.push(
                        Line::raw(sender)
                            .bold()
                            .fg(messaging_service.driver.color())
                            .alignment(alignment)
                    );
                }

                for line in lines {
                    let line = match line.is_empty() {
                        true => " ".repeat(max_length),
                        false => format!("{line:max_length$}")
                    };

                    messages.push(
                        Line::raw(line)
                            .white()
                            .bg(messaging_service.driver.color())
                            .alignment(alignment)
                    );
                }
            }
            else {
                for line in lines {
                    let line = match line.is_empty() {
                        true => " ".repeat(max_length),
                        false => format!("{line:max_length$}")
                    };

                    messages.push(
                        Line::raw(line)
                            .white()
                            .on_gray()
                            .alignment(alignment)
                    );
                }
            }

            let timestamp_format = match Local::now().date_naive() == message.timestamp.date_naive() {
                true => "%H:%M",
                false => "%H:%M %d/%m/%Y"
            };

            messages.push(
                Line::raw(message.timestamp.format(timestamp_format).to_string())
                    .dark_gray()
                    .dim()
                    .alignment(alignment)
            );

            last_sender = &message.sender;
        }

        let messages_paragraph = Paragraph::new(messages)
            .scroll((
                self.discussion_scrollbar.scroll,
                0
            ));
        frame.render_widget(&messages_paragraph, inner_messages_area);

        let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
            .style(Style::new().dark_gray());
        frame.render_stateful_widget(scrollbar, messages_area, &mut self.discussion_scrollbar.state);

        let separator_block = Block::new().borders(Borders::TOP).dark_gray();
        frame.render_widget(separator_block, inner_separator_area);

        frame.render_widget(&self.message_input, inner_text_area_area);
    }
}