diff --git a/src/ui/context.rs b/src/context.rs similarity index 54% rename from src/ui/context.rs rename to src/context.rs index dbce4940..df5c4625 100644 --- a/src/ui/context.rs +++ b/src/context.rs @@ -1,10 +1,17 @@ +use radicle_term as term; + use radicle::cob::issue::{Issue, IssueId}; use radicle::cob::patch::{Patch, PatchId}; +use radicle::crypto::ssh::keystore::MemorySigner; use radicle::prelude::{Id, Project, Signer}; +use radicle::profile::env::RAD_PASSPHRASE; use radicle::Profile; use radicle::storage::git::Repository; use radicle::storage::ReadStorage; + +use term::{passphrase, spinner}; + pub struct Context { profile: Profile, id: Id, @@ -16,12 +23,22 @@ pub struct Context { } impl Context { - pub fn new(profile: Profile, id: Id, project: Project, signer: Box) -> Self { + pub fn new(id: Id) -> Result { + use anyhow::Context; + + let profile = profile()?; + let signer = signer(&profile)?; + let payload = &profile + .storage + .get(signer.public_key(), id)? + .context("No project with such `id` exists")?; + let project = payload.project()?; + let repository = profile.storage.repository(id).unwrap(); let issues = crate::cob::issue::all(&repository).unwrap_or_default(); let patches = crate::cob::patch::all(&repository).unwrap_or_default(); - Self { + Ok(Self { id, profile, project, @@ -29,7 +46,7 @@ impl Context { issues, patches, signer, - } + }) } pub fn profile(&self) -> &Profile { @@ -66,3 +83,27 @@ impl Context { self.patches = crate::cob::patch::all(&self.repository).unwrap_or_default(); } } + +/// Get the default profile. Fails if there is no profile. +fn profile() -> Result { + match Profile::load() { + Ok(profile) => Ok(profile), + Err(_) => Err(anyhow::anyhow!( + "Could not load radicle profile. To setup your radicle profile, run `rad auth`." + )), + } +} + +/// Get the signer. First we try getting it from ssh-agent, otherwise we prompt the user. +fn signer(profile: &Profile) -> anyhow::Result> { + if let Ok(signer) = profile.signer() { + return Ok(signer); + } + let passphrase = passphrase(RAD_PASSPHRASE)?; + let spinner = spinner("Unsealing key..."); + let signer = MemorySigner::load(&profile.keystore, Some(passphrase))?; + + spinner.finish(); + + Ok(signer.boxed()) +} diff --git a/src/issue/app.rs b/src/issue/app.rs index 2350f62c..287ea25d 100644 --- a/src/issue/app.rs +++ b/src/issue/app.rs @@ -5,9 +5,6 @@ mod ui; use anyhow::Result; use radicle::cob::issue::IssueId; -use radicle::identity::{Id, Project}; -use radicle::prelude::Signer; -use radicle::profile::Profile; use tuirealm::application::PollStrategy; use tuirealm::{Application, Frame, NoUserEvent, Sub, SubClause}; @@ -15,7 +12,7 @@ use tuirealm::{Application, Frame, NoUserEvent, Sub, SubClause}; use radicle_tui as tui; use tui::cob; -use tui::ui::context::Context; +use tui::context::Context; use tui::ui::subscription; use tui::ui::theme::{self, Theme}; use tui::PageStack; @@ -105,9 +102,9 @@ pub struct App { /// Creates a new application using a tui-realm-application, mounts all /// components and sets focus to a default one. impl App { - pub fn new(profile: Profile, id: Id, project: Project, signer: Box) -> Self { + pub fn new(context: Context) -> Self { Self { - context: Context::new(profile, id, project, signer), + context, pages: PageStack::default(), theme: theme::default_dark(), quit: false, diff --git a/src/issue/app/page.rs b/src/issue/app/page.rs index c64270cb..cf8d18fa 100644 --- a/src/issue/app/page.rs +++ b/src/issue/app/page.rs @@ -9,7 +9,7 @@ use tuirealm::{AttrValue, Attribute, Frame, NoUserEvent, State, StateValue, Sub, use radicle_tui as tui; use tui::cob; -use tui::ui::context::Context; +use tui::context::Context; use tui::ui::layout; use tui::ui::theme::Theme; use tui::ui::widget::context::{Progress, Shortcuts}; diff --git a/src/issue/app/ui.rs b/src/issue/app/ui.rs index 87d49a41..54c6265e 100644 --- a/src/issue/app/ui.rs +++ b/src/issue/app/ui.rs @@ -10,9 +10,9 @@ use tuirealm::{AttrValue, Attribute, Frame, MockComponent, Props, State}; use radicle_tui as tui; +use tui::context::Context; use tui::ui::cob; use tui::ui::cob::IssueItem; -use tui::ui::context::Context; use tui::ui::theme::Theme; use tui::ui::widget::{Widget, WidgetComponent}; diff --git a/src/issue/main.rs b/src/issue/main.rs index f9169a43..d9a38f5d 100644 --- a/src/issue/main.rs +++ b/src/issue/main.rs @@ -1,20 +1,19 @@ +mod app; + use std::process; -use anyhow::{anyhow, Context}; +use anyhow::anyhow; + +use radicle::profile; + use log::info; use log::LevelFilter; -use radicle::profile; -use radicle::{ - crypto::ssh::keystore::MemorySigner, prelude::Signer, profile::env::RAD_PASSPHRASE, Profile, -}; use radicle_term as term; -use radicle_tui::Window; +use radicle_tui as tui; -use radicle::storage::ReadStorage; -use term::{passphrase, spinner}; - -mod app; +use tui::context; +use tui::Window; pub const NAME: &str = "rad-issue-tui"; pub const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -59,42 +58,12 @@ impl Options { } } -/// Get the default profile. Fails if there is no profile. -pub fn profile() -> Result { - match Profile::load() { - Ok(profile) => Ok(profile), - Err(_) => Err(anyhow::anyhow!( - "Could not load radicle profile. To setup your radicle profile, run `rad auth`." - )), - } -} - -/// Get the signer. First we try getting it from ssh-agent, otherwise we prompt the user. -pub fn signer(profile: &Profile) -> anyhow::Result> { - if let Ok(signer) = profile.signer() { - return Ok(signer); - } - let passphrase = passphrase(RAD_PASSPHRASE)?; - let spinner = spinner("Unsealing key..."); - let signer = MemorySigner::load(&profile.keystore, Some(passphrase))?; - - spinner.finish(); - - Ok(signer.boxed()) -} - fn execute() -> anyhow::Result<()> { let _ = Options::from_env()?; let (_, id) = radicle::rad::cwd() .map_err(|_| anyhow!("this command must be run in the context of a project"))?; - let profile = profile()?; - let signer = signer(&profile)?; - let payload = &profile - .storage - .get(signer.public_key(), id)? - .context("No project with such `id` exists")?; - let project = payload.project()?; + let context = context::Context::new(id)?; let logfile = format!( "{}/rad-issue-tui.log", @@ -104,7 +73,7 @@ fn execute() -> anyhow::Result<()> { info!("Launching window..."); let mut window = Window::default(); - window.run(&mut app::App::new(profile, id, project, signer), 1000 / FPS)?; + window.run(&mut app::App::new(context), 1000 / FPS)?; Ok(()) } diff --git a/src/lib.rs b/src/lib.rs index d5effa09..d38f35d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,9 +9,10 @@ use tuirealm::Frame; use tuirealm::{Application, EventListenerCfg, NoUserEvent}; pub mod cob; +pub mod context; pub mod ui; -use ui::context::Context; +use context::Context; use ui::theme::Theme; /// Trait that must be implemented by client applications in order to be run diff --git a/src/patch/app.rs b/src/patch/app.rs index a979ee74..fcb76c7b 100644 --- a/src/patch/app.rs +++ b/src/patch/app.rs @@ -7,16 +7,14 @@ use std::hash::Hash; use anyhow::Result; use radicle::cob::patch::PatchId; -use radicle::identity::{Id, Project}; -use radicle::prelude::Signer; -use radicle::profile::Profile; use tuirealm::application::PollStrategy; use tuirealm::{Application, Frame, NoUserEvent, Sub, SubClause}; use radicle_tui as tui; + use radicle_tui::cob; -use radicle_tui::ui::context::Context; +use radicle_tui::context::Context; use radicle_tui::ui::subscription; use radicle_tui::ui::theme::{self, Theme}; use radicle_tui::PageStack; @@ -88,9 +86,9 @@ pub struct App { /// Creates a new application using a tui-realm-application, mounts all /// components and sets focus to a default one. impl App { - pub fn new(profile: Profile, id: Id, project: Project, signer: Box) -> Self { + pub fn new(context: Context) -> Self { Self { - context: Context::new(profile, id, project, signer), + context, pages: PageStack::default(), theme: theme::default_dark(), quit: false, diff --git a/src/patch/app/page.rs b/src/patch/app/page.rs index e68605e2..2a39fe68 100644 --- a/src/patch/app/page.rs +++ b/src/patch/app/page.rs @@ -8,7 +8,7 @@ use tuirealm::{Frame, NoUserEvent, State, StateValue, Sub, SubClause}; use radicle_tui as tui; -use tui::ui::context::Context; +use tui::context::Context; use tui::ui::theme::Theme; use tui::ui::widget::context::{Progress, Shortcuts}; use tui::ui::widget::Widget; diff --git a/src/patch/app/ui.rs b/src/patch/app/ui.rs index 1e40b9c0..1216d2e7 100644 --- a/src/patch/app/ui.rs +++ b/src/patch/app/ui.rs @@ -6,9 +6,9 @@ use tuirealm::{AttrValue, Attribute, Frame, MockComponent, Props, State}; use radicle_tui as tui; +use tui::context::Context; use tui::ui::cob; use tui::ui::cob::PatchItem; -use tui::ui::context::Context; use tui::ui::layout; use tui::ui::theme::Theme; use tui::ui::widget::{Widget, WidgetComponent}; diff --git a/src/patch/main.rs b/src/patch/main.rs index b7b0cd91..7d982054 100644 --- a/src/patch/main.rs +++ b/src/patch/main.rs @@ -1,20 +1,19 @@ +mod app; + use std::process; -use anyhow::{anyhow, Context}; +use anyhow::anyhow; + +use radicle::profile; + use log::info; use log::LevelFilter; -use radicle::profile; -use radicle::{ - crypto::ssh::keystore::MemorySigner, prelude::Signer, profile::env::RAD_PASSPHRASE, Profile, -}; use radicle_term as term; -use radicle_tui::Window; +use radicle_tui as tui; -use radicle::storage::ReadStorage; -use term::{passphrase, spinner}; - -mod app; +use tui::context; +use tui::Window; pub const NAME: &str = "rad-patch-tui"; pub const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -24,7 +23,7 @@ pub const FPS: u64 = 60; pub const HELP: &str = r#" Usage -rad-patch-tui [