concord 2.5.0

A terminal user interface client for Discord
use super::*;

const MAX_VISIBLE_DOWNLOADS: usize = 3;
const DOWNLOADS_POPUP_MAX_WIDTH: u16 = 48;

pub(in crate::tui::ui) fn render_downloads_popup(
    frame: &mut Frame,
    area: Rect,
    state: &DashboardState,
) {
    let downloads = state.attachment_downloads();
    if downloads.is_empty() {
        return;
    }

    let line_count = downloads_popup_line_count(downloads.len());
    let popup = downloads_popup_area(area, line_count);
    if popup.is_empty() {
        return;
    }

    let lines = downloads_popup_lines(&downloads, popup.width.saturating_sub(2));

    let inner = render_modal_frame(frame, popup, "Downloads");
    frame.render_widget(Paragraph::new(lines), inner);
}

pub(in crate::tui::ui) fn downloads_popup_line_count(download_count: usize) -> usize {
    download_count.min(MAX_VISIBLE_DOWNLOADS) + usize::from(download_count > MAX_VISIBLE_DOWNLOADS)
}

pub(in crate::tui::ui) fn downloads_popup_area(area: Rect, line_count: usize) -> Rect {
    if area.width < 3 || area.height < 3 || line_count == 0 {
        return Rect::default();
    }

    let width = area.width.min(DOWNLOADS_POPUP_MAX_WIDTH);
    let height = u16::try_from(line_count)
        .unwrap_or(u16::MAX)
        .saturating_add(2)
        .min(area.height);
    Rect {
        x: area.x + area.width.saturating_sub(width),
        y: area.y + area.height.saturating_sub(height),
        width,
        height,
    }
}

pub(in crate::tui::ui) fn downloads_popup_lines(
    downloads: &[AttachmentDownloadProgressView],
    width: u16,
) -> Vec<Line<'static>> {
    let visible_count = downloads.len().min(MAX_VISIBLE_DOWNLOADS);
    let start = downloads.len().saturating_sub(visible_count);
    let mut lines: Vec<Line<'static>> = downloads[start..]
        .iter()
        .map(|download| download_progress_line(download, usize::from(width)))
        .collect();
    let hidden_count = downloads.len().saturating_sub(visible_count);
    if hidden_count > 0 {
        lines.push(Line::from(Span::styled(
            format!("+{hidden_count} more"),
            theme::current().style(theme::HighlightGroup::Hint),
        )));
    }
    lines
}

fn download_progress_line(
    download: &AttachmentDownloadProgressView,
    width: usize,
) -> Line<'static> {
    let status = match download.total_bytes.filter(|total| *total > 0) {
        Some(total) => {
            let percent = download
                .downloaded_bytes
                .saturating_mul(100)
                .checked_div(total)
                .unwrap_or_default()
                .min(100);
            format!(
                "{}% {}/{}",
                percent,
                format_byte_size(download.downloaded_bytes),
                format_byte_size(total)
            )
        }
        None => format!("{} downloaded", format_byte_size(download.downloaded_bytes)),
    };
    let text = format!("{} {status}", download.filename);
    Line::from(Span::raw(truncate_display_width(&text, width)))
}