Radish alpha
r
rad:z39mP9rQAaGmERfUMPULfPUi473tY
Radicle terminal user interface
Radicle
Git
Selection footer improvements
Merged did:key:z6MkswQE...2C1V opened 2 years ago
6 files changed +116 -52 bb104bd4 ee8a14de
modified bin/commands/inbox/flux/select/ui.rs
@@ -1,12 +1,14 @@
-
use std::cmp;
+
use std::collections::HashMap;
use std::vec;

+
use ratatui::style::Stylize;
+
use ratatui::text::Line;
use tokio::sync::mpsc::UnboundedSender;

use termion::event::Key;

use ratatui::backend::Backend;
-
use ratatui::layout::{Constraint, Direction, Layout, Rect};
+
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};

use radicle_tui as tui;

@@ -143,12 +145,26 @@ impl Render<()> for ListPage {

struct NotificationsProps {
    notifications: Vec<NotificationItem>,
+
    stats: HashMap<String, usize>,
}

impl From<&InboxState> for NotificationsProps {
    fn from(state: &InboxState) -> Self {
+
        let mut seen = 0;
+
        let mut unseen = 0;
+

+
        for notification in &state.notifications {
+
            if notification.seen {
+
                seen += 1;
+
            } else {
+
                unseen += 1;
+
            }
+
        }
+
        let stats = HashMap::from([("Seen".to_string(), seen), ("Unseen".to_string(), unseen)]);
+

        Self {
            notifications: state.notifications.clone(),
+
            stats,
        }
    }
}
@@ -257,16 +273,23 @@ impl Render<()> for Notifications {
            Constraint::Length(18),
        ];

-
        let progress = {
-
            let step = self
-
                .table
-
                .selected()
-
                .map(|selected| selected.saturating_add(1).to_string())
-
                .unwrap_or("-".to_string());
-
            let length = self.props.notifications.len().to_string();
-

-
            span::badge(format!("{}/{}", cmp::min(&step, &length), length))
-
        };
+
        let filter = Line::from([span::blank()].to_vec());
+
        let stats = Line::from(
+
            [
+
                span::positive(self.props.stats.get("Seen").unwrap_or(&0).to_string()).dim(),
+
                span::default(" Seen".to_string()).dim(),
+
                span::default(" | ".to_string()).dim(),
+
                span::default(self.props.stats.get("Unseen").unwrap_or(&0).to_string())
+
                    .magenta()
+
                    .dim(),
+
                span::default(" Unseen".to_string()).dim(),
+
            ]
+
            .to_vec(),
+
        )
+
        .alignment(Alignment::Right);
+

+
        let (step, len) = self.table.progress(self.props.notifications.len());
+
        let progress = span::progress(step, len, false);

        self.header.render::<B>(
            frame,
@@ -307,14 +330,8 @@ impl Render<()> for Notifications {
            frame,
            layout[2],
            FooterProps {
-
                cells: [
-
                    span::badge("/".to_string()).into(),
-
                    String::from("").into(),
-
                    String::from("").into(),
-
                    progress.clone().into(),
-
                ],
+
                cells: [filter.into(), stats.into(), progress.clone().into()],
                widths: [
-
                    Constraint::Length(3),
                    Constraint::Fill(1),
                    Constraint::Fill(1),
                    Constraint::Length(progress.width() as u16),
modified bin/commands/issue/flux/select/ui.rs
@@ -1,13 +1,15 @@
-
use std::cmp;
+
use std::collections::HashMap;
use std::vec;

+
use radicle::issue;
use ratatui::style::Stylize;
+
use ratatui::text::Line;
use tokio::sync::mpsc::UnboundedSender;

use termion::event::Key;

use ratatui::backend::Backend;
-
use ratatui::layout::{Constraint, Direction, Layout, Rect};
+
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};

use radicle_tui as tui;

@@ -171,13 +173,26 @@ impl Render<()> for ListPage {
struct IssuesProps {
    issues: Vec<IssueItem>,
    filter: Filter,
+
    stats: HashMap<String, usize>,
}

impl From<&IssuesState> for IssuesProps {
    fn from(state: &IssuesState) -> Self {
+
        let mut open = 0;
+
        let mut closed = 0;
+

+
        for issue in &state.issues {
+
            match issue.state {
+
                issue::State::Open => open += 1,
+
                issue::State::Closed { reason: _ } => closed += 1,
+
            }
+
        }
+
        let stats = HashMap::from([("Open".to_string(), open), ("Closed".to_string(), closed)]);
+

        Self {
            issues: state.issues.clone(),
            filter: state.filter.clone(),
+
            stats,
        }
    }
}
@@ -286,17 +301,36 @@ impl Render<()> for Issues {
            Constraint::Length(16),
        ];

-
        let progress = {
-
            let step = self
-
                .table
-
                .selected()
-
                .map(|selected| selected.saturating_add(1).to_string())
-
                .unwrap_or("-".to_string());
-
            let length = self.props.issues.len().to_string();
-

-
            span::badge(format!("{}/{}", cmp::min(&step, &length), length))
+
        let filter = if !self.props.filter.to_string().is_empty() {
+
            Line::from(
+
                [
+
                    span::badge("▼".to_string()),
+
                    span::default(" ".to_string()),
+
                    span::default(self.props.filter.to_string()).magenta().dim(),
+
                ]
+
                .to_vec(),
+
            )
+
        } else {
+
            Line::from([span::blank()].to_vec())
        };

+
        let stats = Line::from(
+
            [
+
                span::positive(self.props.stats.get("Open").unwrap_or(&0).to_string()).dim(),
+
                span::default(" Open".to_string()).dim(),
+
                span::default(" | ".to_string()).dim(),
+
                span::default(self.props.stats.get("Closed").unwrap_or(&0).to_string())
+
                    .magenta()
+
                    .dim(),
+
                span::default(" Closed".to_string()).dim(),
+
            ]
+
            .to_vec(),
+
        )
+
        .alignment(Alignment::Right);
+

+
        let (step, len) = self.table.progress(self.props.issues.len());
+
        let progress = span::progress(step, len, false);
+

        self.header.render::<B>(
            frame,
            layout[0],
@@ -336,17 +370,8 @@ impl Render<()> for Issues {
            frame,
            layout[2],
            FooterProps {
-
                cells: [
-
                    span::badge("/".to_string()).into(),
-
                    span::default(self.props.filter.to_string())
-
                        .magenta()
-
                        .dim()
-
                        .into(),
-
                    String::from("").into(),
-
                    progress.clone().into(),
-
                ],
+
                cells: [filter.into(), stats.into(), progress.clone().into()],
                widths: [
-
                    Constraint::Length(3),
                    Constraint::Fill(1),
                    Constraint::Fill(1),
                    Constraint::Length(progress.width() as u16),
modified bin/commands/patch/flux/select/ui.rs
@@ -369,10 +369,19 @@ impl Render<()> for Patches {
            },
        );

-
        let (step, len) = self.table.progress(self.props.patches.len());
+
        let filter = if !self.props.filter.to_string().is_empty() {
+
            Line::from(
+
                [
+
                    span::badge("▼".to_string()),
+
                    span::default(" ".to_string()),
+
                    span::default(self.props.filter.to_string()).magenta().dim(),
+
                ]
+
                .to_vec(),
+
            )
+
        } else {
+
            Line::from([span::blank()].to_vec())
+
        };

-
        let progress = span::badge(format!("{}/{}", step, len));
-
        let filter = span::default(self.props.filter.to_string()).magenta().dim();
        let stats = Line::from(
            [
                span::default(self.props.stats.get("Draft").unwrap_or(&0).to_string()).dim(),
@@ -395,18 +404,15 @@ impl Render<()> for Patches {
        )
        .alignment(Alignment::Right);

+
        let (step, len) = self.table.progress(self.props.patches.len());
+
        let progress = span::progress(step, len, false);
+

        self.footer.render::<B>(
            frame,
            layout[2],
            FooterProps {
-
                cells: [
-
                    span::badge("/".to_string()).into(),
-
                    filter.into(),
-
                    stats.into(),
-
                    progress.clone().into(),
-
                ],
+
                cells: [filter.into(), stats.into(), progress.clone().into()],
                widths: [
-
                    Constraint::Length(3),
                    Constraint::Fill(1),
                    Constraint::Fill(1),
                    Constraint::Length(progress.width() as u16),
modified src/common/cob/issue.rs
@@ -90,7 +90,6 @@ impl Filter {
impl ToString for Filter {
    fn to_string(&self) -> String {
        let mut filter = String::new();
-
        filter.push(' ');

        if let Some(state) = &self.state {
            filter.push_str(&format!("is:{}", state));
modified src/common/cob/patch.rs
@@ -82,7 +82,6 @@ impl Filter {
impl ToString for Filter {
    fn to_string(&self) -> String {
        let mut filter = String::new();
-
        filter.push(' ');

        if let Some(state) = &self.state {
            filter.push_str(&format!("is:{}", state));
modified src/flux/ui/span.rs
@@ -55,3 +55,21 @@ pub fn notification_id(content: String) -> Span<'static> {
pub fn notification_type(content: String) -> Span<'static> {
    default(content).style(style::gray().dim())
}
+

+
pub fn progress(step: usize, len: usize, fill_zeros: bool) -> Span<'static> {
+
    if fill_zeros {
+
        if len > 10 {
+
            badge(format!("{:-02}/{:-02}", step, len))
+
        } else if len > 100 {
+
            badge(format!("{:-03}/{:-03}", step, len))
+
        } else if len > 1000 {
+
            badge(format!("{:-04}/{:-04}", step, len))
+
        } else if len > 10000 {
+
            badge(format!("{:-05}/{:-05}", step, len))
+
        } else {
+
            badge(format!("{}/{}", step, len))
+
        }
+
    } else {
+
        badge(format!("{}/{}", step, len))
+
    }
+
}