http-diff 0.0.5

http-diff - CLI tool to verify consistency across web server versions. Ideal for large-scale refactors, sanity tests and maintaining data integrity across versions.
use crate::app_state::AppState;
use ratatui::{prelude::*, widgets::*};

fn notification_rect(r: Rect) -> Rect {
    let popup_vertical_layout = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Percentage(85),
            Constraint::Max(7),
            Constraint::Min(0),
        ])
        .split(r);

    Layout::default()
        .direction(Direction::Horizontal)
        .constraints([
            Constraint::Percentage(3),
            Constraint::Max(70),
            Constraint::Min(9),
        ])
        .split(popup_vertical_layout[1])[1]
}

pub fn render_notification(frame: &mut Frame, app: &mut AppState) {
    if let Some(notification) = &app.notification {
        let popup_area = notification_rect(frame.size());

        frame.render_widget(Clear, popup_area);

        let popup_layout = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Percentage(85),
                Constraint::Percentage(15),
            ])
            .split(popup_area);

        let block = Block::default().bg(match notification.r#type {
            NotificationType::Error => app.theme.error,
            NotificationType::Warning => app.theme.warning,
            NotificationType::Success => app.theme.success,
        });

        frame.render_widget(block, popup_area);

        let block = Block::default()
            .padding(Padding::horizontal(1))
            .borders(Borders::ALL)
            .title(match notification.r#type {
                NotificationType::Error => "Error",
                NotificationType::Warning => "Warning",
                NotificationType::Success => "Success",
            })
            .style(match notification.r#type {
                NotificationType::Error => {
                    Style::default().fg(app.theme.black)
                }
                NotificationType::Warning => {
                    Style::default().fg(app.theme.black)
                }
                NotificationType::Success => {
                    Style::default().fg(app.theme.white)
                }
            });

        let body: Vec<Line<'_>> = notification
            .body
            .as_str()
            .lines()
            .map(|line| Line::from(line.fg(app.theme.black)))
            .collect();

        let paragraph = Paragraph::new(body)
            .wrap(Wrap { trim: true })
            .alignment(Alignment::Left)
            .block(block);

        if let Some(percentage) = notification.get_show_percentage_left() {
            let gauge = Gauge::default()
                .gauge_style(Style::new().gray())
                .percent(percentage as u16);

            frame.render_widget(gauge, popup_layout[1]);
        } else {
            let block = Block::default().padding(Padding::horizontal(1));

            let paragraph = Paragraph::new("Press `Esc` to dismiss".gray())
                .wrap(Wrap { trim: true })
                .alignment(Alignment::Right)
                .block(block);

            frame.render_widget(paragraph, popup_layout[1]);
        }

        frame.render_widget(paragraph, popup_layout[0]);
    }
}

use std::time::{Duration, Instant};

#[derive(Clone, Debug, PartialEq)]
pub enum NotificationId {
    NoFailedJobs,
    SavedJob,
    SavedJobs,
    FailedToSaveJobs,
    PendingJobInfoError,
    JobProgressChange,
    GenerateDefaultConfig,
    GenerateDefaultConfigFailed,
    AllRequestsFinishedWithoutFails,
    AllRequestsFinishedWithFails,
    ReloadingConfiguration,
}

#[derive(Clone, Debug, PartialEq)]
pub enum NotificationType {
    Success,
    Warning,
    Error,
}

#[derive(Clone, Debug, PartialEq)]
pub struct Notification {
    pub id: NotificationId,
    pub body: String,
    pub expire_duration: Option<Duration>,
    pub started_at: Instant,
    pub r#type: NotificationType,
}

impl Notification {
    pub fn new(
        id: NotificationId,
        body: &str,
        expire_duration: Option<Duration>,
        r#type: NotificationType,
    ) -> Self {
        Self {
            id,
            expire_duration,
            body: body.to_owned(),
            started_at: Instant::now(),
            r#type,
        }
    }

    pub fn get_show_percentage_left(&self) -> Option<u64> {
        if let Some(expire) = self.expire_duration {
            let now = Instant::now();

            let left = now - self.started_at;

            return Some(100 - (left.as_secs() * 100) / expire.as_secs());
        };

        None
    }
}