Skip to content

Commit

Permalink
feat: add version info to bottom right of screen
Browse files Browse the repository at this point in the history
- as this is fast-changing I have added version info to the bottom right of the screen
- If enabled in config, will attempt to fetch tag of latest version on load
- if latest version is not the same as the current version, will indicate that the current version is out of date
- can turn this off in the config, at which point it will just show the latest version
  • Loading branch information
robertpsoane committed Jul 7, 2024
1 parent 32934ee commit c85b3a6
Show file tree
Hide file tree
Showing 8 changed files with 463 additions and 24 deletions.
331 changes: 318 additions & 13 deletions Cargo.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ ratatui = { version = "0.27.0", features = [
"serde",
"unstable-rendered-line-info",
] }
reqwest = { version = "0.12.5", features = ["json"] }
serde = "1.0.203"
serde_yml = "0.0.10"
tokio = { version = "1.38.0", features = [
Expand Down
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,15 @@ Ducker is configured via a yaml file found in the relevant config directory for

The following table summarises the available config values:

| Key | Default | Description |
| ------------ | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| prompt | 🦆 | The default prompt to display in the command pane |
| default_exec | `/bin/bash` | The default prompt to display in the command pane. NB - currently uses this for all exec's; it is planned to offer a choice |
| docker_path | `unix:///var/run/docker.sock` | The location of the socket on which the docker daemon is exposed (defaults to `npipe:////./pipe/docker_engine` on windows) |
| theme | [See below] | The colour theme configuration |
| Key | Default | Description |
| ---------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| prompt | 🦆 | The default prompt to display in the command pane |
| default_exec | `/bin/bash` | The default prompt to display in the command pane. NB - currently uses this for all exec's; it is planned to offer a choice |
| docker_path | `unix:///var/run/docker.sock` | The location of the socket on which the docker daemon is exposed (defaults to `npipe:////./pipe/docker_engine` on windows) |
| check_for_update | `true` | When true, checks whether there is a newer version on load. If a newer version is found, indicates via note in bottom right.* |
| theme | [See below] | The colour theme configuration |

> :warning: **This is not available in v0.0.5 and below as released on cargo**: to use this feature, install the unstable build or await next release
If a value is unset or if the config file is unfound, Ducker will use the default values. If a value is malformed, Ducker will fail to run.

Expand Down
23 changes: 19 additions & 4 deletions src/components/footer.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,39 @@
use itertools::Itertools;
use ratatui::{
layout::Rect,
layout::{Constraint, Layout, Rect},
style::{Modifier, Style},
text::{Line, Span},
Frame,
};

use crate::{config::Config, traits::Component};

use super::version::VersionComponent;

#[derive(Debug)]
pub struct Footer {
config: Box<Config>,
version: VersionComponent,
}

impl Footer {
pub fn new(config: Box<Config>) -> Self {
Self { config }
pub async fn new(config: Box<Config>) -> Self {
Self {
config: config.clone(),
version: VersionComponent::new(config).await,
}
}
}

impl Component for Footer {
fn draw(&mut self, f: &mut Frame<'_>, area: Rect) {
let layout = Layout::horizontal([
Constraint::Length(20),
Constraint::Min(0),
Constraint::Length(20),
]);
let [_left, mid, right] = layout.areas(area);

let keys = [
("K/↑", "Up"),
("J/↓", "Down"),
Expand Down Expand Up @@ -48,6 +61,8 @@ impl Component for Footer {

let footer = Line::from(spans).centered().style(Style::new());

f.render_widget(footer, area)
f.render_widget(footer, mid);

self.version.draw(f, right)
}
}
1 change: 1 addition & 0 deletions src/components/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ pub mod header;
pub mod help;
pub mod resize_notice;
pub mod text_input_wrapper;
pub mod version;
106 changes: 106 additions & 0 deletions src/components/version.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use crate::{config::Config, traits::Component};
use color_eyre::eyre::Result;
use ratatui::{
layout::{Margin, Rect},
style::Style,
text::{Line, Span},
Frame,
};
use reqwest::header::USER_AGENT;

const VERSION: &str = env!("CARGO_PKG_VERSION");
const TAGS_URL: &str = "https://api.github.com/repos/robertpsoane/ducker/tags";

#[derive(Debug)]
pub struct VersionComponent {
config: Box<Config>,
version: String,
update_to: Option<String>,
}

impl VersionComponent {
pub async fn new(config: Box<Config>) -> Self {
let version = format!("v{VERSION}");

let update_to = if config.check_for_update {
get_update_to(&version).await
} else {
None
};

Self {
config,
version,
update_to,
}
}
}

impl Component for VersionComponent {
fn draw(&mut self, f: &mut Frame<'_>, area: Rect) {
let area = area.inner(Margin {
vertical: 0,
horizontal: 1,
});

let current_version = Span::from(self.version.clone());

let spans = if let Some(update) = &self.update_to {
let update_to_span = Span::from(update);
let arrow = Span::from(" > ");
vec![
current_version.style(Style::default().fg(self.config.theme.negative_highlight())),
arrow,
update_to_span.style(Style::default().fg(self.config.theme.positive_highlight())),
]
} else {
vec![current_version.style(Style::default().fg(self.config.theme.positive_highlight()))]
};

f.render_widget(
Line::from(spans).alignment(ratatui::layout::Alignment::Right),
area,
)
}
}

async fn get_update_to(version: &str) -> Option<String> {
let latest_version = match find_latest_version().await {
Ok(v) => v,
Err(_) => return None,
};
if version == latest_version {
None
} else {
Some(latest_version)
}
}

async fn find_latest_version() -> Result<String> {
let client = reqwest::Client::new();

let body: serde_yml::Value = client
.get(TAGS_URL)
.header(USER_AGENT, format!("Ducker / {VERSION}"))
.send()
.await?
.json()
.await?;

let release = match body.get(0) {
Some(v) => v,
None => panic!("could not parse response"),
};

let release_name = match release.get("name") {
Some(v) => v,
None => panic!("could not parse response"),
};

let release_name_value = match release_name.as_str() {
Some(v) => String::from(v),
None => panic!("could not parse response"),
};

Ok(release_name_value)
}
8 changes: 8 additions & 0 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ pub struct Config {
#[serde(default = "default_docker_path")]
pub docker_path: String,

#[serde(default = "default_check_update")]
pub check_for_update: bool,

#[serde(default)]
pub theme: Theme,
}
Expand Down Expand Up @@ -62,6 +65,10 @@ fn default_docker_path() -> String {
return "npipe:////./pipe/docker_engine".into();
}

fn default_check_update() -> bool {
return true;

Check warning on line 69 in src/config.rs

View workflow job for this annotation

GitHub Actions / check / stable / clippy

unneeded `return` statement

warning: unneeded `return` statement --> src/config.rs:69:5 | 69 | return true; | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return = note: `#[warn(clippy::needless_return)]` on by default help: remove `return` | 69 - return true; 69 + true |

Check warning on line 69 in src/config.rs

View workflow job for this annotation

GitHub Actions / check / beta / clippy

unneeded `return` statement

warning: unneeded `return` statement --> src/config.rs:69:5 | 69 | return true; | ^^^^^^^^^^^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_return = note: `#[warn(clippy::needless_return)]` on by default help: remove `return` | 69 - return true; 69 + true |
}

fn default_use_theme() -> bool {
false
}
Expand All @@ -72,6 +79,7 @@ impl Default for Config {
prompt: default_prompt(),
default_exec: default_exec(),
docker_path: default_docker_path(),
check_for_update: default_check_update(),
theme: Theme::default(),
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/ui/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl App {
resize_screen: ResizeScreen::new(config.clone()),
title: Header::new(config.clone()),
page_manager: body,
footer: Footer::new(config.clone()),
footer: Footer::new(config.clone()).await,
input_field: CommandInput::new(tx, config.prompt),
modal: None,
};
Expand Down

0 comments on commit c85b3a6

Please sign in to comment.