refactor: Improve app context creation

This commit is contained in:
Erik Kundt
2023-10-06 10:55:38 +02:00
parent f4eaf5c17e
commit f58c93a36f
11 changed files with 81 additions and 107 deletions
+44 -3
View File
@@ -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<dyn Signer>) -> Self {
pub fn new(id: Id) -> Result<Self, anyhow::Error> {
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<Profile, anyhow::Error> {
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<Box<dyn Signer>> {
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())
}
+3 -6
View File
@@ -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<dyn Signer>) -> 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,
+1 -1
View File
@@ -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};
+1 -1
View File
@@ -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};
+11 -42
View File
@@ -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<Profile, anyhow::Error> {
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<Box<dyn Signer>> {
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(())
}
+2 -1
View File
@@ -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
+4 -6
View File
@@ -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<dyn Signer>) -> 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,
+1 -1
View File
@@ -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;
+1 -1
View File
@@ -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};
+12 -43
View File
@@ -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 [<option>...]
rad-patch-tui [<option>...]
Options
@@ -59,42 +58,12 @@ impl Options {
}
}
/// Get the default profile. Fails if there is no profile.
pub fn profile() -> Result<Profile, anyhow::Error> {
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<Box<dyn Signer>> {
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-patch-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(())
}
+1 -2
View File
@@ -1,5 +1,4 @@
pub mod cob;
pub mod context;
pub mod ext;
pub mod layout;
pub mod state;
@@ -20,7 +19,7 @@ use widget::list::{ColumnWidth, Property, PropertyList, PropertyTable};
use widget::Widget;
use crate::ui::context::Context;
use super::context::Context;
use crate::ui::theme::Theme;
pub fn global_listener() -> Widget<GlobalListener> {