Radish alpha
r
rad:z39mP9rQAaGmERfUMPULfPUi473tY
Radicle terminal user interface
Radicle
Git
bin/inbox: Return to app after clearing
Merged did:key:z6MkgFq6...nBGz opened 4 months ago
6 files changed +115 -88 1ddae1e3 1592a0d7
modified bin/commands/inbox.rs
@@ -9,14 +9,15 @@ use anyhow::anyhow;

use radicle::storage::{HasRepoId, ReadRepository};

-
use radicle_cli::terminal;
use radicle_cli::terminal::{Args, Error, Help};

-
use self::common::RepositoryMode;
-

use crate::commands::tui_inbox::common::InboxOperation;
+
use crate::terminal;
+
use crate::terminal::Quiet;
use crate::ui::items::notification::filter::{NotificationFilter, SortBy};

+
use self::common::RepositoryMode;
+

pub const HELP: Help = Help {
    name: "inbox",
    description: "Terminal interfaces for notifications",
@@ -101,8 +102,9 @@ impl Args for Options {
                    reverse = Some(true);
                }
                Long("sort-by") => {
-
                    let val = parser.value()?;
+
                    use radicle_cli::terminal;

+
                    let val = parser.value()?;
                    match terminal::args::string(&val).as_str() {
                        "timestamp" => field = Some("timestamp"),
                        "id" => field = Some("id"),
@@ -111,9 +113,10 @@ impl Args for Options {
                }

                Long("repo") if repository_mode.is_none() => {
+
                    use radicle_cli::terminal;
+

                    let val = parser.value()?;
                    let repo = terminal::args::rid(&val)?;
-

                    repository_mode = Some(RepositoryMode::ByRepo((repo, None)));
                }
                Long("all") | Short('a') if repository_mode.is_none() => {
@@ -172,7 +175,7 @@ impl Args for Options {
}

#[tokio::main]
-
pub async fn run(options: Options, ctx: impl terminal::Context) -> anyhow::Result<()> {
+
pub async fn run(options: Options, ctx: impl radicle_cli::terminal::Context) -> anyhow::Result<()> {
    use radicle::storage::ReadStorage;

    let (_, rid) = radicle::rad::cwd()
@@ -180,54 +183,63 @@ pub async fn run(options: Options, ctx: impl terminal::Context) -> anyhow::Resul

    match options.op {
        Operation::List { opts } => {
-
            let profile = ctx.profile()?;
-
            let repository = profile.storage.repository(rid)?;
-

            if let Err(err) = crate::log::enable() {
                println!("{err}");
            }
            log::info!("Starting inbox listing interface in project {rid}..");

-
            let context = list::Context {
-
                profile,
-
                project: repository.identity_doc()?.project()?,
-
                rid: repository.rid(),
-
                mode: opts.mode,
-
                filter: opts.filter.clone(),
-
                sort_by: opts.sort_by,
-
            };
-
            let selection = list::Tui::new(context).run().await?;
-

-
            if opts.json {
-
                let selection = selection
-
                    .map(|o| serde_json::to_string(&o).unwrap_or_default())
-
                    .unwrap_or_default();
-

-
                log::info!("About to print to `stderr`: {selection}");
-
                log::info!("Exiting inbox listing interface..");
-

-
                eprint!("{selection}");
-
            } else if let Some(selection) = selection {
-
                if let Some(operation) = selection.operation.clone() {
-
                    match operation {
-
                        InboxOperation::Show { id } => {
-
                            let _ = crate::terminal::run_rad(
-
                                Some("inbox"),
-
                                &[OsString::from("show"), OsString::from(id.to_string())],
-
                            );
-
                        }
-
                        InboxOperation::Clear { id } => {
-
                            let _ = crate::terminal::run_rad(
-
                                Some("inbox"),
-
                                &[OsString::from("clear"), OsString::from(id.to_string())],
-
                            );
+
            loop {
+
                let profile = ctx.profile()?;
+
                let repository = profile.storage.repository(rid)?;
+

+
                let context = list::Context {
+
                    profile,
+
                    project: repository.identity_doc()?.project()?,
+
                    rid: repository.rid(),
+
                    mode: opts.mode.clone(),
+
                    filter: opts.filter.clone(),
+
                    sort_by: opts.sort_by,
+
                };
+

+
                let app = list::Tui::new(context);
+
                let selection = app.run().await?;
+

+
                if opts.json {
+
                    let selection = selection
+
                        .map(|o| serde_json::to_string(&o).unwrap_or_default())
+
                        .unwrap_or_default();
+

+
                    log::info!("About to print to `stderr`: {selection}");
+
                    log::info!("Exiting inbox listing interface..");
+

+
                    eprint!("{selection}");
+
                } else if let Some(selection) = selection {
+
                    if let Some(operation) = selection.operation.clone() {
+
                        match operation {
+
                            InboxOperation::Show { id } => {
+
                                terminal::run_rad(
+
                                    Some("inbox"),
+
                                    &["show".into(), id.to_string().into()],
+
                                    Quiet::No,
+
                                )?;
+
                                break;
+
                            }
+
                            InboxOperation::Clear { id } => {
+
                                terminal::run_rad(
+
                                    Some("inbox"),
+
                                    &["clear".into(), id.to_string().into()],
+
                                    Quiet::Yes,
+
                                )?;
+
                            }
                        }
                    }
+
                } else {
+
                    break;
                }
            }
        }
        Operation::Other { args } => {
-
            let _ = crate::terminal::run_rad(Some("inbox"), &args);
+
            terminal::run_rad(Some("inbox"), &args, Quiet::No)?;
        }
        Operation::Unknown { .. } => {
            anyhow::bail!("unknown operation provided");
modified bin/commands/inbox/list.rs
@@ -415,7 +415,7 @@ impl App {
    }

    fn show_browser_footer(&self, frame: &mut Frame, ui: &mut im::Ui<Message>) {
-
        ui.layout(Layout::vertical([1, 1]), None, |ui| {
+
        ui.layout(Layout::vertical([3, 1]), None, |ui| {
            self.show_browser_context(frame, ui);
            self.show_browser_shortcuts(frame, ui);
        });
@@ -482,7 +482,9 @@ impl App {
                    Column::new(
                        Span::raw(format!(" {search} "))
                            .into_left_aligned_line()
-
                            .style(ui.theme().bar_on_black_style),
+
                            .style(ui.theme().bar_on_black_style)
+
                            .cyan()
+
                            .dim(),
                        Constraint::Fill(1),
                    ),
                    Column::new(
@@ -705,16 +707,14 @@ impl Task for NotificationLoader {

    fn run(&self) -> anyhow::Result<Vec<Self::Return>> {
        let profile = self.context.profile.clone();
+
        let notifs = profile.notifications_mut()?;

        let notifications = match self.context.mode {
            RepositoryMode::All => {
-
                let notifs = profile.notifications_mut()?;
-

                // Store all repos the notifs arised from, such that
                // they can be referenced when loading issues and patches
-
                let all = notifs.all()?;
-
                let repos = all
-
                    .into_iter()
+
                let repos = notifs
+
                    .all()?
                    .filter_map(|notif| notif.ok())
                    .filter_map(|notif| {
                        profile
@@ -726,9 +726,10 @@ impl Task for NotificationLoader {
                    .collect::<HashMap<_, _>>();

                // Only retrieve issues and patches once per repository
-
                let all = notifs.all()?;
                let (mut issues, mut patches) = (HashMap::new(), HashMap::new());
-
                all.filter_map(|notif| notif.ok())
+
                notifs
+
                    .all()?
+
                    .filter_map(|notif| notif.ok())
                    .map(|notif| match repos.get(&notif.repo) {
                        Some(repo) => {
                            let project = repo.project()?;
@@ -745,7 +746,7 @@ impl Task for NotificationLoader {
                                )
                            };

-
                            match NotificationKind::new(&repo, issues, patches, &notif)? {
+
                            match NotificationKind::new(repo, issues, patches, &notif)? {
                                Some(kind) => Notification::new(&profile, &project, &notif, kind),
                                _ => Ok(None),
                            }
@@ -761,18 +762,16 @@ impl Task for NotificationLoader {
                let project = repo.project()?;
                let issues = profile.issues(&repo)?;
                let patches = profile.patches(&repo)?;
-
                let notifs = profile.notifications_mut()?;
                let by_repo = notifs.by_repo(&repo.id, "timestamp")?;

                by_repo
                    .filter_map(|notif| notif.ok())
-
                    .map(|notif| {
-
                        let repo = self.context.profile.storage.repository(notif.repo)?;
-
                        match NotificationKind::new(&repo, &issues, &patches, &notif)? {
+
                    .map(
+
                        |notif| match NotificationKind::new(&repo, &issues, &patches, &notif)? {
                            Some(kind) => Notification::new(&profile, &project, &notif, kind),
                            _ => Ok(None),
-
                        }
-
                    })
+
                        },
+
                    )
                    .filter_map(|notif| notif.ok())
                    .flatten()
                    .collect::<Vec<_>>()
@@ -782,18 +781,16 @@ impl Task for NotificationLoader {
                let project = repo.project()?;
                let issues = profile.issues(&repo)?;
                let patches = profile.patches(&repo)?;
-
                let notifs = profile.notifications_mut()?;
                let by_repo = notifs.by_repo(&repo.id, "timestamp")?;

                by_repo
                    .filter_map(|notif| notif.ok())
-
                    .map(|notif| {
-
                        let repo = self.context.profile.storage.repository(notif.repo)?;
-
                        match NotificationKind::new(&repo, &issues, &patches, &notif)? {
+
                    .map(
+
                        |notif| match NotificationKind::new(&repo, &issues, &patches, &notif)? {
                            Some(kind) => Notification::new(&profile, &project, &notif, kind),
                            _ => Ok(None),
-
                        }
-
                    })
+
                        },
+
                    )
                    .filter_map(|notif| notif.ok())
                    .flatten()
                    .collect::<Vec<_>>()
modified bin/commands/issue.rs
@@ -22,7 +22,7 @@ use cli::terminal::{Args, Error, Help};

use crate::cob;
use crate::commands::tui_issue::common::IssueOperation;
-
use crate::terminal;
+
use crate::terminal::{self, Quiet};
use crate::ui::TerminalInfo;

lazy_static! {
@@ -234,17 +234,19 @@ pub async fn run(options: Options, ctx: impl Context) -> anyhow::Result<()> {
                    if let Some(operation) = selection.operation.clone() {
                        match operation {
                            IssueOperation::Show { id } => {
-
                                let _ = terminal::run_rad(
+
                                terminal::run_rad(
                                    Some("issue"),
                                    &["show".into(), id.to_string().into()],
-
                                );
+
                                    Quiet::No,
+
                                )?;
                                break;
                            }
                            IssueOperation::Edit { id } => {
-
                                let _ = terminal::run_rad(
+
                                terminal::run_rad(
                                    Some("issue"),
-
                                    &["edit".into(), id.to_string().into(), "--quiet".into()],
-
                                );
+
                                    &["edit".into(), id.to_string().into()],
+
                                    Quiet::Yes,
+
                                )?;
                            }
                            IssueOperation::Comment {
                                id,
@@ -272,7 +274,7 @@ pub async fn run(options: Options, ctx: impl Context) -> anyhow::Result<()> {
            }
        }
        Operation::Other { args } => {
-
            let _ = crate::terminal::run_rad(Some("issue"), &args);
+
            terminal::run_rad(Some("issue"), &args, Quiet::No)?;
        }
        Operation::Unknown { .. } => {
            anyhow::bail!("unknown operation provided");
modified bin/commands/patch.rs
@@ -20,6 +20,7 @@ use radicle_cli::terminal::args::{string, Args, Error, Help};

use crate::cob::patch;
use crate::commands::tui_patch::common::PatchOperation;
+
use crate::terminal::Quiet;

pub const HELP: Help = Help {
    name: "patch",
@@ -266,22 +267,25 @@ pub async fn run(options: Options, ctx: impl terminal::Context) -> anyhow::Resul
                if let Some(operation) = selection.operation.clone() {
                    match operation {
                        PatchOperation::Show { id } => {
-
                            let _ = crate::terminal::run_rad(
+
                            crate::terminal::run_rad(
                                Some("patch"),
-
                                &[OsString::from("show"), OsString::from(id.to_string())],
-
                            );
+
                                &["show".into(), id.to_string().into()],
+
                                Quiet::No,
+
                            )?;
                        }
                        PatchOperation::Diff { id } => {
-
                            let _ = crate::terminal::run_rad(
+
                            crate::terminal::run_rad(
                                Some("patch"),
-
                                &[OsString::from("diff"), OsString::from(id.to_string())],
-
                            );
+
                                &["diff".into(), id.to_string().into()],
+
                                Quiet::No,
+
                            )?;
                        }
                        PatchOperation::Checkout { id } => {
-
                            let _ = crate::terminal::run_rad(
+
                            crate::terminal::run_rad(
                                Some("patch"),
-
                                &[OsString::from("checkout"), OsString::from(id.to_string())],
-
                            );
+
                                &["checkout".into(), id.to_string().into()],
+
                                Quiet::No,
+
                            )?;
                        }
                        PatchOperation::_Review { id } => {
                            let opts = ReviewOptions::default();
@@ -308,7 +312,7 @@ pub async fn run(options: Options, ctx: impl terminal::Context) -> anyhow::Resul
            interface::review(opts.clone(), profile, rid, patch_id).await?;
        }
        Operation::Other { args } => {
-
            let _ = crate::terminal::run_rad(Some("patch"), &args);
+
            crate::terminal::run_rad(Some("patch"), &args, Quiet::No)?;
        }
        Operation::Unknown { .. } => {
            anyhow::bail!("unknown operation provided");
modified bin/main.rs
@@ -23,7 +23,7 @@ use radicle_cli::terminal as cli_term;
use commands::*;
use terminal as term;

-
use crate::terminal::ForwardError;
+
use crate::terminal::{ForwardError, Quiet};

pub const NAME: &str = "rad-tui";
pub const DESCRIPTION: &str = "Radicle terminal interfaces";
@@ -224,7 +224,7 @@ fn run_other(command: Option<&str>, args: &[OsString]) -> Result<(), Error> {
                args.to_vec(),
            );
        }
-
        command => term::run_rad(command, args).map_err(|err| err.into()),
+
        command => term::run_rad(command, args, Quiet::No).map_err(|err| err.into()),
    }
}

modified bin/terminal.rs
@@ -12,6 +12,11 @@ use radicle_cli::terminal::io;
use radicle_cli::terminal::patch::Message;
use radicle_cli::terminal::{Args, Command, DefaultContext, Error, Help};

+
pub enum Quiet {
+
    Yes,
+
    No,
+
}
+

#[derive(Error, Debug)]
pub enum ForwardError {
    #[error("an internal error occured while executing 'rad'")]
@@ -20,8 +25,15 @@ pub enum ForwardError {
    Io(#[from] std::io::Error),
}

-
fn _run_rad(args: &[OsString]) -> Result<(), ForwardError> {
-
    let status = process::Command::new("rad").args(args).status();
+
fn _run_rad(args: &[OsString], quiet: Quiet) -> Result<(), ForwardError> {
+
    let status = match quiet {
+
        Quiet::Yes => process::Command::new("rad")
+
            .args(args)
+
            .stdout(process::Stdio::null())
+
            .stderr(process::Stdio::null())
+
            .status(),
+
        Quiet::No => process::Command::new("rad").args(args).status(),
+
    };

    match status {
        Ok(status) => {
@@ -34,14 +46,14 @@ fn _run_rad(args: &[OsString]) -> Result<(), ForwardError> {
    }
}

-
pub fn run_rad(command: Option<&str>, args: &[OsString]) -> Result<(), ForwardError> {
+
pub fn run_rad(command: Option<&str>, args: &[OsString], quiet: Quiet) -> Result<(), ForwardError> {
    let args = if let Some(command) = command {
        [vec![command.into()], args.to_vec()].concat()
    } else {
        args.to_vec()
    };

-
    _run_rad(&args)
+
    _run_rad(&args, quiet)
}

pub fn run_command_args<A, C>(help: Help, cmd: C, args: Vec<OsString>) -> !