Radish alpha
r
rad:z39mP9rQAaGmERfUMPULfPUi473tY
Radicle terminal user interface
Radicle
Git
bin: Fix and clean up selection state
Merged did:key:z6MkswQE...2C1V opened 2 years ago
7 files changed +155 -153 f657892d bc59201f
modified bin/commands/inbox/common.rs
@@ -1,3 +1,7 @@
+
use std::fmt::Display;
+

+
use serde::Serialize;
+

use radicle::identity::RepoId;

/// The application's subject. It tells the application
@@ -44,3 +48,24 @@ impl Mode {
        &self.repository
    }
}
+

+
/// The selected issue operation returned by the operation
+
/// selection widget.
+
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
+
pub enum InboxOperation {
+
    Show,
+
    Clear,
+
}
+

+
impl Display for InboxOperation {
+
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+
        match self {
+
            InboxOperation::Show => {
+
                write!(f, "show")
+
            }
+
            InboxOperation::Clear => {
+
                write!(f, "clear")
+
            }
+
        }
+
    }
+
}
modified bin/commands/inbox/flux/select.rs
@@ -58,7 +58,6 @@ impl Default for UIState {
#[derive(Clone, Debug)]
pub struct State {
    notifications: Vec<NotificationItem>,
-
    selected: Option<NotificationItem>,
    mode: Mode,
    project: Project,
    search: StateValue<String>,
@@ -152,11 +151,8 @@ impl TryFrom<&Context> for State {
            notifications.sort_by(|a, b| a.project.cmp(&b.project));
        }

-
        let selected = notifications.first().cloned();
-

        Ok(Self {
            notifications,
-
            selected,
            mode: mode.clone(),
            project,
            search: StateValue::new(String::new()),
@@ -167,7 +163,6 @@ impl TryFrom<&Context> for State {

pub enum Action {
    Exit { selection: Option<Selection> },
-
    Select { item: NotificationItem },
    PageSize(usize),
    OpenSearch,
    UpdateSearch { value: String },
@@ -183,10 +178,6 @@ impl store::State<Action, Selection> for State {
    fn handle_action(&mut self, action: Action) -> Option<Exit<Selection>> {
        match action {
            Action::Exit { selection } => Some(Exit { value: selection }),
-
            Action::Select { item } => {
-
                self.selected = Some(item);
-
                None
-
            }
            Action::PageSize(size) => {
                self.ui.page_size = size;
                None
modified bin/commands/inbox/flux/select/ui.rs
@@ -24,12 +24,11 @@ use tui::flux::ui::widget::{
};
use tui::Selection;

-
use crate::tui_inbox::common::{Mode, RepositoryMode, SelectionMode};
+
use crate::tui_inbox::common::{InboxOperation, Mode, RepositoryMode, SelectionMode};

use super::{Action, State};

pub struct ListPageProps {
-
    selected: Option<NotificationItem>,
    mode: Mode,
    show_search: bool,
    show_help: bool,
@@ -38,7 +37,6 @@ pub struct ListPageProps {
impl From<&State> for ListPageProps {
    fn from(state: &State) -> Self {
        Self {
-
            selected: state.selected.clone(),
            mode: state.mode.clone(),
            show_search: state.ui.show_search,
            show_help: state.ui.show_help,
@@ -104,30 +102,6 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
                Key::Esc | Key::Ctrl('c') => {
                    let _ = self.action_tx.send(Action::Exit { selection: None });
                }
-
                Key::Char('\n') => {
-
                    if let Some(selected) = &self.props.selected {
-
                        let selection = match self.props.mode.selection() {
-
                            SelectionMode::Operation => Selection::default()
-
                                .with_operation("show".to_string())
-
                                .with_id(selected.id),
-
                            SelectionMode::Id => Selection::default().with_id(selected.id),
-
                        };
-
                        let _ = self.action_tx.send(Action::Exit {
-
                            selection: Some(selection),
-
                        });
-
                    }
-
                }
-
                Key::Char('c') => {
-
                    if let Some(selected) = &self.props.selected {
-
                        let _ = self.action_tx.send(Action::Exit {
-
                            selection: Some(
-
                                Selection::default()
-
                                    .with_operation("clear".to_string())
-
                                    .with_id(selected.id),
-
                            ),
-
                        });
-
                    }
-
                }
                Key::Char('/') => {
                    let _ = self.action_tx.send(Action::OpenSearch);
                }
@@ -321,18 +295,43 @@ impl Widget<State, Action> for Notifications {
            Key::End => {
                self.table.end(self.props.notifications.len());
            }
+
            Key::Char('\n') => {
+
                self.table
+
                    .selected()
+
                    .and_then(|selected| self.props.notifications.get(selected))
+
                    .and_then(|notif| {
+
                        let selection = match self.props.mode.selection() {
+
                            SelectionMode::Operation => Selection::default()
+
                                .with_operation(InboxOperation::Show.to_string())
+
                                .with_id(notif.id),
+
                            SelectionMode::Id => Selection::default().with_id(notif.id),
+
                        };
+

+
                        self.action_tx
+
                            .send(Action::Exit {
+
                                selection: Some(selection),
+
                            })
+
                            .ok()
+
                    });
+
            }
+
            Key::Char('c') => {
+
                self.table
+
                    .selected()
+
                    .and_then(|selected| self.props.notifications.get(selected))
+
                    .and_then(|notif| {
+
                        self.action_tx
+
                            .send(Action::Exit {
+
                                selection: Some(
+
                                    Selection::default()
+
                                        .with_operation(InboxOperation::Clear.to_string())
+
                                        .with_id(notif.id),
+
                                ),
+
                            })
+
                            .ok()
+
                    });
+
            }
            _ => {}
        }
-
        self.table
-
            .selected()
-
            .and_then(|selected| self.props.notifications.get(selected))
-
            .and_then(|notif| {
-
                self.action_tx
-
                    .send(Action::Select {
-
                        item: notif.clone(),
-
                    })
-
                    .ok()
-
            });
    }
}

modified bin/commands/issue/flux/select.rs
@@ -53,7 +53,6 @@ impl Default for UIState {
#[derive(Clone, Debug)]
pub struct State {
    issues: Vec<IssueItem>,
-
    selected: Option<IssueItem>,
    mode: Mode,
    search: StateValue<String>,
    ui: UIState,
@@ -73,11 +72,8 @@ impl TryFrom<&Context> for State {
            }
        }

-
        let selected = items.first().cloned();
-

        Ok(Self {
            issues: items,
-
            selected,
            mode: context.mode.clone(),
            search: StateValue::new(context.filter.to_string()),
            ui: UIState::default(),
@@ -87,7 +83,6 @@ impl TryFrom<&Context> for State {

pub enum Action {
    Exit { selection: Option<Selection> },
-
    Select { item: IssueItem },
    PageSize(usize),
    OpenSearch,
    UpdateSearch { value: String },
@@ -103,10 +98,6 @@ impl store::State<Action, Selection> for State {
    fn handle_action(&mut self, action: Action) -> Option<Exit<Selection>> {
        match action {
            Action::Exit { selection } => Some(Exit { value: selection }),
-
            Action::Select { item } => {
-
                self.selected = Some(item);
-
                None
-
            }
            Action::PageSize(size) => {
                self.ui.page_size = size;
                None
modified bin/commands/issue/flux/select/ui.rs
@@ -30,7 +30,6 @@ use crate::tui_issue::common::Mode;
use super::{Action, State};

pub struct ListPageProps {
-
    selected: Option<IssueItem>,
    mode: Mode,
    show_search: bool,
    show_help: bool,
@@ -39,7 +38,6 @@ pub struct ListPageProps {
impl From<&State> for ListPageProps {
    fn from(state: &State) -> Self {
        Self {
-
            selected: state.selected.clone(),
            mode: state.mode.clone(),
            show_search: state.ui.show_search,
            show_help: state.ui.show_help,
@@ -105,32 +103,6 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
                Key::Esc | Key::Ctrl('c') => {
                    let _ = self.action_tx.send(Action::Exit { selection: None });
                }
-
                Key::Char('\n') => {
-
                    if let Some(selected) = &self.props.selected {
-
                        let operation = match self.props.mode {
-
                            Mode::Operation => Some(IssueOperation::Show.to_string()),
-
                            Mode::Id => None,
-
                        };
-
                        let _ = self.action_tx.send(Action::Exit {
-
                            selection: Some(Selection {
-
                                operation,
-
                                ids: vec![selected.id],
-
                                args: vec![],
-
                            }),
-
                        });
-
                    }
-
                }
-
                Key::Char('e') => {
-
                    if let Some(selected) = &self.props.selected {
-
                        let _ = self.action_tx.send(Action::Exit {
-
                            selection: Some(Selection {
-
                                operation: Some(IssueOperation::Edit.to_string()),
-
                                ids: vec![selected.id],
-
                                args: vec![],
-
                            }),
-
                        });
-
                    }
-
                }
                Key::Char('/') => {
                    let _ = self.action_tx.send(Action::OpenSearch);
                }
@@ -197,6 +169,7 @@ impl<'a> Render<()> for ListPage<'a> {
}

struct IssuesProps {
+
    mode: Mode,
    issues: Vec<IssueItem>,
    search: String,
    stats: HashMap<String, usize>,
@@ -250,6 +223,7 @@ impl From<&State> for IssuesProps {
        ]);

        Self {
+
            mode: state.mode.clone(),
            issues,
            search: state.search.read(),
            widths: [
@@ -343,18 +317,45 @@ impl Widget<State, Action> for Issues {
            Key::End => {
                self.table.end(self.props.issues.len());
            }
+
            Key::Char('\n') => {
+
                let operation = match self.props.mode {
+
                    Mode::Operation => Some(IssueOperation::Show.to_string()),
+
                    Mode::Id => None,
+
                };
+

+
                self.table
+
                    .selected()
+
                    .and_then(|selected| self.props.issues.get(selected))
+
                    .and_then(|issue| {
+
                        self.action_tx
+
                            .send(Action::Exit {
+
                                selection: Some(Selection {
+
                                    operation,
+
                                    ids: vec![issue.id],
+
                                    args: vec![],
+
                                }),
+
                            })
+
                            .ok()
+
                    });
+
            }
+
            Key::Char('e') => {
+
                self.table
+
                    .selected()
+
                    .and_then(|selected| self.props.issues.get(selected))
+
                    .and_then(|issue| {
+
                        self.action_tx
+
                            .send(Action::Exit {
+
                                selection: Some(Selection {
+
                                    operation: Some(IssueOperation::Edit.to_string()),
+
                                    ids: vec![issue.id],
+
                                    args: vec![],
+
                                }),
+
                            })
+
                            .ok()
+
                    });
+
            }
            _ => {}
        }
-
        self.table
-
            .selected()
-
            .and_then(|selected| self.props.issues.get(selected))
-
            .and_then(|issue| {
-
                self.action_tx
-
                    .send(Action::Select {
-
                        item: issue.clone(),
-
                    })
-
                    .ok()
-
            });
    }
}

modified bin/commands/patch/flux/select.rs
@@ -53,7 +53,6 @@ impl Default for UIState {
#[derive(Clone, Debug)]
pub struct State {
    patches: Vec<PatchItem>,
-
    selected: Option<PatchItem>,
    mode: Mode,
    search: store::StateValue<String>,
    ui: UIState,
@@ -73,11 +72,8 @@ impl TryFrom<&Context> for State {
            }
        }

-
        let selected = items.first().cloned();
-

        Ok(Self {
            patches: items,
-
            selected,
            mode: context.mode.clone(),
            search: store::StateValue::new(context.filter.to_string()),
            ui: UIState::default(),
@@ -87,7 +83,6 @@ impl TryFrom<&Context> for State {

pub enum Action {
    Exit { selection: Option<Selection> },
-
    Select { item: PatchItem },
    PageSize(usize),
    OpenSearch,
    UpdateSearch { value: String },
@@ -103,10 +98,6 @@ impl store::State<Action, Selection> for State {
    fn handle_action(&mut self, action: Action) -> Option<Exit<Selection>> {
        match action {
            Action::Exit { selection } => Some(Exit { value: selection }),
-
            Action::Select { item } => {
-
                self.selected = Some(item);
-
                None
-
            }
            Action::PageSize(size) => {
                self.ui.page_size = size;
                None
modified bin/commands/patch/flux/select/ui.rs
@@ -32,7 +32,6 @@ use crate::tui_patch::common::PatchOperation;
use super::{Action, State};

pub struct ListPageProps {
-
    selected: Option<PatchItem>,
    mode: Mode,
    show_search: bool,
    show_help: bool,
@@ -41,7 +40,6 @@ pub struct ListPageProps {
impl From<&State> for ListPageProps {
    fn from(state: &State) -> Self {
        Self {
-
            selected: state.selected.clone(),
            mode: state.mode.clone(),
            show_search: state.ui.show_search,
            show_help: state.ui.show_help,
@@ -108,45 +106,6 @@ impl<'a> Widget<State, Action> for ListPage<'a> {
                Key::Esc | Key::Ctrl('c') => {
                    let _ = self.action_tx.send(Action::Exit { selection: None });
                }
-
                Key::Char('\n') => {
-
                    if let Some(selected) = &self.props.selected {
-
                        let operation = match self.props.mode {
-
                            Mode::Operation => Some(PatchOperation::Show.to_string()),
-
                            Mode::Id => None,
-
                        };
-
                        let _ = self.action_tx.send(Action::Exit {
-
                            selection: Some(Selection {
-
                                operation,
-
                                ids: vec![selected.id],
-
                                args: vec![],
-
                            }),
-
                        });
-
                    }
-
                }
-
                Key::Char('c') => {
-
                    if let Some(selected) = &self.props.selected {
-
                        let selection = Selection {
-
                            operation: Some(PatchOperation::Checkout.to_string()),
-
                            ids: vec![selected.id],
-
                            args: vec![],
-
                        };
-
                        let _ = self.action_tx.send(Action::Exit {
-
                            selection: Some(selection),
-
                        });
-
                    }
-
                }
-
                Key::Char('d') => {
-
                    if let Some(selected) = &self.props.selected {
-
                        let selection = Selection {
-
                            operation: Some(PatchOperation::Diff.to_string()),
-
                            ids: vec![selected.id],
-
                            args: vec![],
-
                        };
-
                        let _ = self.action_tx.send(Action::Exit {
-
                            selection: Some(selection),
-
                        });
-
                    }
-
                }
                Key::Char('/') => {
                    let _ = self.action_tx.send(Action::OpenSearch);
                }
@@ -214,6 +173,7 @@ impl<'a> Render<()> for ListPage<'a> {
}

struct PatchesProps {
+
    mode: Mode,
    patches: Vec<PatchItem>,
    search: StateValue<String>,
    stats: HashMap<String, usize>,
@@ -263,6 +223,7 @@ impl From<&State> for PatchesProps {
        ]);

        Self {
+
            mode: state.mode.clone(),
            patches,
            search: state.search.clone(),
            widths: [
@@ -357,18 +318,61 @@ impl Widget<State, Action> for Patches {
            Key::End => {
                self.table.end(self.props.patches.len());
            }
+
            Key::Char('\n') => {
+
                let operation = match self.props.mode {
+
                    Mode::Operation => Some(PatchOperation::Show.to_string()),
+
                    Mode::Id => None,
+
                };
+

+
                self.table
+
                    .selected()
+
                    .and_then(|selected| self.props.patches.get(selected))
+
                    .and_then(|patch| {
+
                        self.action_tx
+
                            .send(Action::Exit {
+
                                selection: Some(Selection {
+
                                    operation,
+
                                    ids: vec![patch.id],
+
                                    args: vec![],
+
                                }),
+
                            })
+
                            .ok()
+
                    });
+
            }
+
            Key::Char('c') => {
+
                self.table
+
                    .selected()
+
                    .and_then(|selected| self.props.patches.get(selected))
+
                    .and_then(|patch| {
+
                        self.action_tx
+
                            .send(Action::Exit {
+
                                selection: Some(Selection {
+
                                    operation: Some(PatchOperation::Checkout.to_string()),
+
                                    ids: vec![patch.id],
+
                                    args: vec![],
+
                                }),
+
                            })
+
                            .ok()
+
                    });
+
            }
+
            Key::Char('d') => {
+
                self.table
+
                    .selected()
+
                    .and_then(|selected| self.props.patches.get(selected))
+
                    .and_then(|patch| {
+
                        self.action_tx
+
                            .send(Action::Exit {
+
                                selection: Some(Selection {
+
                                    operation: Some(PatchOperation::Diff.to_string()),
+
                                    ids: vec![patch.id],
+
                                    args: vec![],
+
                                }),
+
                            })
+
                            .ok()
+
                    });
+
            }
            _ => {}
        }
-
        self.table
-
            .selected()
-
            .and_then(|selected| self.props.patches.get(selected))
-
            .and_then(|patch| {
-
                self.action_tx
-
                    .send(Action::Select {
-
                        item: patch.clone(),
-
                    })
-
                    .ok()
-
            });
    }
}