Radish alpha
r
Radicle terminal user interface
Radicle
Git (anonymous pull)
Log in to clone via SSH
bin/inbox: Fix default filter and its rendering
Erik Kundt committed 6 months ago
commit 904586f8c6a83b9f19b429c54e43f1c2b2067825
parent fe60e62dd784890f45080b2a8a15e19e2577b63e
5 files changed +120 -20
modified bin/cob/inbox.rs
@@ -4,9 +4,6 @@ use radicle::node::notifications::Notification;
use radicle::storage::git::Repository;
use radicle::Profile;

-
#[derive(Clone, Default, Debug, Eq, PartialEq)]
-
pub struct Filter {}
-

#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SortBy {
    pub reverse: bool,
modified bin/commands/inbox.rs
@@ -13,6 +13,7 @@ use radicle_cli::terminal::{Args, Error, Help};
use self::common::{Mode, RepositoryMode, SelectionMode};

use crate::cob::inbox;
+
use crate::ui::items::notification::filter::NotificationFilter;

pub const HELP: Help = Help {
    name: "inbox",
@@ -62,7 +63,7 @@ pub enum OperationName {
#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct ListOptions {
    mode: Mode,
-
    filter: inbox::Filter,
+
    filter: NotificationFilter,
    sort_by: inbox::SortBy,
    json: bool,
}
modified bin/commands/inbox/list.rs
@@ -51,7 +51,7 @@ pub struct Context {
    pub profile: Profile,
    pub repository: Repository,
    pub mode: Mode,
-
    pub filter: inbox::Filter,
+
    pub filter: NotificationFilter,
    pub sort_by: inbox::SortBy,
}

@@ -105,8 +105,7 @@ impl TryFrom<&Context> for State {
        let doc = context.repository.identity_doc()?;
        let project = doc.project()?;

-
        let search = BufferedValue::new(String::new());
-
        let filter = NotificationFilter::from_str(&search.read()).unwrap_or_default();
+
        let search = BufferedValue::new(context.filter.to_string());

        let mut notifications = match &context.mode.repository() {
            RepositoryMode::All => {
@@ -195,7 +194,7 @@ impl TryFrom<&Context> for State {
            browser: BrowserState {
                items: notifications,
                selected: Some(0),
-
                filter,
+
                filter: context.filter.clone(),
                search,
                show_search: false,
            },
@@ -235,8 +234,8 @@ impl store::Update<Message> for State {
            }
            Message::UpdateSearch { value } => {
                self.browser.search.write(value);
-
                self.browser.filter =
-
                    NotificationFilter::from_str(&self.browser.search.read()).unwrap_or_default();
+
                self.browser.filter = NotificationFilter::from_str(&self.browser.search.read())
+
                    .unwrap_or(NotificationFilter::Invalid);

                if let Some(selected) = self.browser.selected {
                    if selected > self.browser.notifications().len() {
@@ -254,8 +253,8 @@ impl store::Update<Message> for State {
            Message::CloseSearch => {
                self.browser.search.reset();
                self.browser.show_search = false;
-
                self.browser.filter =
-
                    NotificationFilter::from_str(&self.browser.search.read()).unwrap_or_default();
+
                self.browser.filter = NotificationFilter::from_str(&self.browser.search.read())
+
                    .unwrap_or(NotificationFilter::Invalid);

                None
            }
modified bin/ui/items.rs
@@ -52,6 +52,7 @@ use super::super::git;
use super::format;

pub mod filter {
+
    use std::fmt::{self, Write};
    use std::str::FromStr;

    use nom::bytes::complete::{tag_no_case, take};
@@ -69,12 +70,32 @@ pub mod filter {
        fn matches(&self, item: &T) -> bool;
    }

-
    #[derive(Debug, Clone, PartialEq)]
+
    #[derive(Debug, Clone, PartialEq, Eq)]
    pub enum DidFilter {
        Single(Did),
        Or(Vec<Did>),
    }

+
    impl fmt::Display for DidFilter {
+
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+
            match self {
+
                DidFilter::Single(did) => write!(f, "{did}")?,
+
                DidFilter::Or(dids) => {
+
                    let mut it = dids.iter().peekable();
+
                    f.write_char('(')?;
+
                    while let Some(did) = it.next() {
+
                        write!(f, "{did}")?;
+
                        if it.peek().is_none() {
+
                            write!(f, " or ")?;
+
                        }
+
                    }
+
                    f.write_char(')')?;
+
                }
+
            }
+
            Ok(())
+
        }
+
    }
+

    fn parse_did(input: &str) -> IResult<&str, Did> {
        match Did::from_str(input) {
            Ok(did) => IResult::Ok(("", did)),
modified bin/ui/items/notification.rs
@@ -1,23 +1,22 @@
-
use radicle::cob::{ObjectId, Timestamp, Title, TypedId};
+
use std::fmt;

+
use radicle::cob::{ObjectId, Timestamp, Title, TypedId};
use radicle::identity::Identity;
-

use radicle::issue::Issues;
use radicle::node;
-

use radicle::patch::Patches;
use radicle::prelude::Project;
use radicle::storage::git::Repository;
use radicle::storage::{ReadRepository, RefUpdate};
use radicle::Profile;

-
use radicle_tui::ui::theme::style;
use ratatui::style::Stylize;
use ratatui::widgets::Cell;

use radicle_tui as tui;

use tui::ui::span;
+
use tui::ui::theme::style;
use tui::ui::ToRow;

use super::AuthorItem;
@@ -30,12 +29,34 @@ pub enum NotificationType {
    Unknown,
}

+
impl fmt::Display for NotificationType {
+
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+
        match self {
+
            NotificationType::Patch => write!(f, "patch")?,
+
            NotificationType::Issue => write!(f, "issue")?,
+
            NotificationType::Branch => write!(f, "branch")?,
+
            NotificationType::Unknown => write!(f, "unknown")?,
+
        }
+
        Ok(())
+
    }
+
}
+

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum NotificationState {
    Seen,
    Unseen,
}

+
impl fmt::Display for NotificationState {
+
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+
        match self {
+
            NotificationState::Seen => write!(f, "seen")?,
+
            NotificationState::Unseen => write!(f, "unseen")?,
+
        }
+
        Ok(())
+
    }
+
}
+

#[derive(Clone, Debug)]
#[allow(dead_code)]
pub enum NotificationKind {
@@ -289,6 +310,8 @@ impl ToRow<9> for Notification {
}

pub mod filter {
+
    use std::fmt;
+
    use std::fmt::Write as _;
    use std::str::FromStr;

    use nom::branch::alt;
@@ -304,13 +327,15 @@ pub mod filter {

    use super::{Notification, NotificationKind, NotificationState, NotificationType};

-
    #[derive(Debug, Clone, PartialEq)]
+
    #[derive(Debug, Clone, PartialEq, Eq)]
    pub enum NotificationFilter {
        State(NotificationState),
        Type(NotificationTypeFilter),
        Author(DidFilter),
        Search(String),
        And(Vec<NotificationFilter>),
+
        Empty,
+
        Invalid,
    }

    impl Default for NotificationFilter {
@@ -322,12 +347,67 @@ pub mod filter {
        }
    }

-
    #[derive(Debug, Clone, PartialEq)]
+
    impl fmt::Display for NotificationFilter {
+
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+
            match self {
+
                NotificationFilter::State(state) => {
+
                    write!(f, "state={state}")?;
+
                    f.write_char(' ')?;
+
                }
+
                NotificationFilter::Type(filter) => {
+
                    write!(f, "type={filter}")?;
+
                    f.write_char(' ')?;
+
                }
+
                NotificationFilter::Author(filter) => {
+
                    write!(f, "author={filter}")?;
+
                    f.write_char(' ')?;
+
                }
+
                NotificationFilter::Search(search) => {
+
                    write!(f, "{search}")?;
+
                    f.write_char(' ')?;
+
                }
+
                NotificationFilter::And(filters) => {
+
                    let mut it = filters.iter().peekable();
+
                    while let Some(filter) = it.next() {
+
                        write!(f, "{filter}")?;
+
                        if it.peek().is_none() {
+
                            f.write_char(' ')?;
+
                        }
+
                    }
+
                }
+
                NotificationFilter::Empty | NotificationFilter::Invalid => {}
+
            }
+

+
            Ok(())
+
        }
+
    }
+

+
    #[derive(Debug, Clone, PartialEq, Eq)]
    pub enum NotificationTypeFilter {
        Single(NotificationType),
        Or(Vec<NotificationType>),
    }

+
    impl fmt::Display for NotificationTypeFilter {
+
        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+
            match self {
+
                NotificationTypeFilter::Single(type_name) => write!(f, "{type_name}")?,
+
                NotificationTypeFilter::Or(type_names) => {
+
                    let mut it = type_names.iter().peekable();
+
                    f.write_char('(')?;
+
                    while let Some(type_name) = it.next() {
+
                        write!(f, "{type_name}")?;
+
                        if it.peek().is_some() {
+
                            write!(f, " or ")?;
+
                        }
+
                    }
+
                    f.write_char(')')?;
+
                }
+
            }
+
            Ok(())
+
        }
+
    }
+

    impl Filter<Notification> for NotificationFilter {
        fn matches(&self, notif: &Notification) -> bool {
            use fuzzy_matcher::skim::SkimMatcherV2;
@@ -395,6 +475,8 @@ pub mod filter {
                    }
                }
                NotificationFilter::And(filters) => filters.iter().all(|f| f.matches(notif)),
+
                NotificationFilter::Empty => true,
+
                NotificationFilter::Invalid => false,
            }
        }
    }
@@ -511,7 +593,7 @@ pub mod filter {
                        }

                        if filters.is_empty() {
-
                            return Err("No filters provided".to_string());
+
                            return Ok(NotificationFilter::Empty);
                        }

                        if filters.len() == 1 {