Skip to content

Commit

Permalink
feat: add the containers scene (#224)
Browse files Browse the repository at this point in the history
  • Loading branch information
therustmonk committed Aug 8, 2023
1 parent 04a95bf commit 038a9bb
Show file tree
Hide file tree
Showing 13 changed files with 250 additions and 12 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ tui-textarea = "0.2.0"

tari_launchpad_protocol = { path = "../libs/protocol" }
tari_sdm_assets = { path = "../libs/sdm-assets" }
tari_sdm = { path = "../libs/sdm" }
tari_sdm_launchpad = { path = "../libs/sdm-launchpad" }
tari_sim_launchpad = { path = "../libs/sim-launchpad", optional = true }

Expand Down
106 changes: 106 additions & 0 deletions cli/src/component/expert/containers/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
use std::{borrow::Cow, cell::RefCell};

use tari_sdm::ids::{ManagedTask, TaskId};
use tari_sdm_launchpad::resources::images;
use tui::{
backend::Backend,
layout::{Constraint, Layout, Rect},
style::{Color, Style},
widgets::{Row, Table, TableState},
};

use crate::{
component::{elements::block_with_title, AppState, Component, ComponentEvent, Frame, Input, Pass},
state::focus,
};

pub struct ContainersScene {
table_state: RefCell<TableState>,
containers: Vec<TaskId>,
}

impl ContainersScene {
pub fn new() -> Self {
Self {
table_state: RefCell::new(TableState::default()),
containers: containers(),
}
}
}

impl Input for ContainersScene {
fn on_event(&mut self, event: ComponentEvent, state: &mut AppState) {
if state.focus_on == focus::CONTAINERS_TABLE {
match event.pass() {
Pass::Up | Pass::Leave => {
state.focus_on(focus::ROOT);
},
_ => {},
}
}
}
}

impl<B: Backend> Component<B> for ContainersScene {
type State = AppState;

fn draw(&self, f: &mut Frame<B>, rect: Rect, state: &Self::State) {
let block = block_with_title(Some("Containers"), state.focus_on == focus::CONTAINERS_TABLE);
let rects = Layout::default()
.constraints([Constraint::Percentage(100)].as_ref())
.split(rect);
let mut rows = Vec::new();
for task_id in &self.containers {
if let Some(task_state) = state.state.containers.get(task_id) {
let col_1 = Cow::Borrowed(task_id.as_ref());
let mut col_2 = Cow::Borrowed("-");
let mut col_3 = Cow::Borrowed("-");
let mut col_4 = Cow::Borrowed("Inactive");
if task_state.status.is_started() {
if let Some(stat_data) = task_state.stats.last() {
let cpu_usage = task_state.stats.last_cpu().unwrap_or_default();
let usage = format!("{:.2} %", cpu_usage);
let mem = stat_data.mem_usage.get_appropriate_unit(false).to_string();
col_2 = Cow::Owned(usage);
col_3 = Cow::Owned(mem);
col_4 = Cow::Owned(task_state.status.to_string());
}
}
let items = vec![col_1, col_2, col_3, col_4];
let row = Row::new(items).height(2);
rows.push(row);
}
}
let header_cells = ["Container", "CPU", "Memory", "Status"];
let header = Row::new(header_cells)
.style(Style::default().fg(Color::Yellow))
.height(1)
.bottom_margin(1);
let table = Table::new(rows)
.block(block)
.header(header)
.widths(&[
Constraint::Length(14),
Constraint::Length(10),
Constraint::Length(10),
Constraint::Min(100),
])
.column_spacing(2);
f.render_stateful_widget(table, rects[0], &mut *self.table_state.borrow_mut());
}
}

fn containers() -> Vec<TaskId> {
vec![
images::Tor::id(),
images::TariBaseNode::id(),
images::TariWallet::id(),
images::TariSha3Miner::id(),
images::MmProxy::id(),
images::Monerod::id(),
images::XMRig::id(),
images::Grafana::id(),
images::Loki::id(),
images::Promtail::id(),
]
}
12 changes: 10 additions & 2 deletions cli/src/component/expert/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod containers;
mod logs;

use containers::ContainersScene;
use logs::LogsScene;
use strum::{Display, EnumCount, EnumIter, FromRepr};
use tui::{
Expand Down Expand Up @@ -34,13 +36,15 @@ impl TabGetter for ExpertTabs {
pub struct ExpertScene {
expert_tabs: AppTabs<ExpertTabs>,
logs_scene: LogsScene,
containers_scene: ContainersScene,
}

impl ExpertScene {
pub fn new() -> Self {
Self {
expert_tabs: AppTabs::new(),
logs_scene: LogsScene::new(),
containers_scene: ContainersScene::new(),
}
}
}
Expand All @@ -50,7 +54,9 @@ impl Input for ExpertScene {
self.expert_tabs.on_event(event, state);
match self.expert_tabs.selected() {
ExpertTabs::Performance => {},
ExpertTabs::Containers => {},
ExpertTabs::Containers => {
self.containers_scene.on_event(event, state);
},
ExpertTabs::Logs => {
self.logs_scene.on_event(event, state);
},
Expand All @@ -70,7 +76,9 @@ impl<B: Backend> Component<B> for ExpertScene {
self.expert_tabs.draw(f, chunks[0], state);
match self.expert_tabs.selected() {
ExpertTabs::Performance => {},
ExpertTabs::Containers => {},
ExpertTabs::Containers => {
self.containers_scene.draw(f, chunks[1], state);
},
ExpertTabs::Logs => {
self.logs_scene.draw(f, chunks[1], state);
},
Expand Down
8 changes: 6 additions & 2 deletions cli/src/component/normal/base_node/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@ use crate::{
struct BaseNodeHint;

impl HintGetter for BaseNodeHint {
fn get_hint(&self, _state: &AppState) -> String {
"Base Node is already running!".into()
fn get_hint(&self, state: &AppState) -> String {
if state.state.config.session.is_base_node_active() {
"Base Node is already running!".into()
} else {
"Begin by starting the Base Node".into()
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion cli/src/component/normal/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl TabGetter for NormalTabs {
match self {
Self::Mining => focus::TARI_MINING,
Self::BaseNode => focus::BASE_NODE,
Self::Wallet => focus::PASSWORD,
Self::Wallet => focus::WALLET_CONTAINER,
}
}
}
Expand Down
102 changes: 102 additions & 0 deletions cli/src/component/normal/wallet/container.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use std::time::Duration;

use tui::{
backend::Backend,
layout::{Constraint, Direction, Layout, Rect},
};

use crate::{
component::{
elements::{block_with_title, logo},
normal::chrono_button::{ChronoButton, ChronoGetter},
Component,
ComponentEvent,
Frame,
Input,
Pass,
},
state::{focus, AppState},
};

const LOGO: &str = r#"
╦ ╦┌─┐┬ ┬ ┌─┐┌┬┐
║║║├─┤│ │ ├┤ │
╚╩╝┴ ┴┴─┘┴─┘└─┘ ┴
"#;

struct WalletContainerGetter;

impl ChronoGetter for WalletContainerGetter {
fn get_duration(&self, _state: &AppState) -> Option<Duration> {
None
}

fn get_label(&self, state: &AppState) -> &str {
if state.state.config.session.is_wallet_active() {
"Pause"
} else {
"Start wallet"
}
}
}

pub struct WalletContainerWidget {
button: ChronoButton<WalletContainerGetter>,
}

impl WalletContainerWidget {
pub fn new() -> Self {
Self {
button: ChronoButton::new(WalletContainerGetter),
}
}
}

impl Input for WalletContainerWidget {
fn on_event(&mut self, event: ComponentEvent, state: &mut AppState) {
if state.focus_on == focus::WALLET_CONTAINER {
match event.pass() {
Pass::Up | Pass::Leave => {
state.focus_on(focus::ROOT);
},
Pass::Enter | Pass::Space => {
let session = &mut state.state.config.session;
session.wallet_active = !session.wallet_active;
state.update_state();
},
_ => {},
}
}
}
}

impl<B: Backend> Component<B> for WalletContainerWidget {
type State = AppState;

fn draw(&self, f: &mut Frame<B>, rect: Rect, state: &Self::State) {
let block = block_with_title(Some("Wallet"), state.focus_on == focus::WALLET_CONTAINER);
let inner_rect = block.inner(rect);
f.render_widget(block, rect);

let constraints = [
Constraint::Length(1),
Constraint::Length(3),
// Constraint::Percentage(50),
Constraint::Length(1),
Constraint::Min(0),
Constraint::Length(1),
];
let v_chunks = Layout::default()
.direction(Direction::Vertical)
.constraints(constraints)
.split(inner_rect);
// self.status_badge.draw(f, v_chunks[0], state);

let logo = logo(LOGO);
f.render_widget(logo, v_chunks[1]);

// self.tari_amount.draw(f, v_chunks[2], state);

self.button.draw(f, v_chunks[4], state);
}
}
11 changes: 8 additions & 3 deletions cli/src/component/normal/wallet/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
mod container;
mod password;

use container::WalletContainerWidget;
use password::PasswordWidget;
use tui::{
backend::Backend,
Expand All @@ -13,18 +15,22 @@ use crate::{

pub struct WalletScene {
password: PasswordWidget,
container: WalletContainerWidget,
}

impl WalletScene {
pub fn new() -> Self {
Self {
password: PasswordWidget::new(),
container: WalletContainerWidget::new(),
}
}
}

impl Input for WalletScene {
fn on_event(&mut self, event: ComponentEvent, state: &mut AppState) {
// TODO: Check the wallet is locked/unlocked
self.container.on_event(event, state);
self.password.on_event(event, state);
}
}
Expand All @@ -45,8 +51,7 @@ impl<B: Backend> Component<B> for WalletScene {
.direction(Direction::Horizontal)
.constraints(constraints)
.split(v_chunks[1]);
self.password.draw(f, h_chunks[0], state);
// self.tari_mining.draw(f, h_chunks[0], state);
// self.merged_mining.draw(f, h_chunks[1], state);
self.container.draw(f, h_chunks[0], state);
// self.password.draw(f, h_chunks[0], state);
}
}
10 changes: 9 additions & 1 deletion cli/src/component/normal/wallet/password.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use crate::{
component::{
elements::{block_with_title, logo},
normal::chrono_button::{ChronoButton, ChronoGetter},
widgets::LabeledInput,
Component,
ComponentEvent,
Frame,
Expand Down Expand Up @@ -41,12 +42,14 @@ impl ChronoGetter for PasswordWidgetGetter {
}

pub struct PasswordWidget {
password: LabeledInput,
button: ChronoButton<PasswordWidgetGetter>,
}

impl PasswordWidget {
pub fn new() -> Self {
Self {
password: LabeledInput::new("Password"),
button: ChronoButton::new(PasswordWidgetGetter),
}
}
Expand All @@ -55,6 +58,7 @@ impl PasswordWidget {
impl Input for PasswordWidget {
fn on_event(&mut self, event: ComponentEvent, state: &mut AppState) {
if state.focus_on == focus::PASSWORD {
self.password.set_focus(true);
match event.pass() {
Pass::Up | Pass::Leave => {
state.focus_on(focus::ROOT);
Expand All @@ -64,6 +68,8 @@ impl Input for PasswordWidget {
},
_ => {},
}
} else {
self.password.set_focus(false);
}
}
}
Expand All @@ -82,6 +88,7 @@ impl<B: Backend> Component<B> for PasswordWidget {
// Constraint::Percentage(50),
Constraint::Length(1),
Constraint::Min(0),
Constraint::Length(3),
Constraint::Length(1),
];
let v_chunks = Layout::default()
Expand All @@ -95,6 +102,7 @@ impl<B: Backend> Component<B> for PasswordWidget {

// self.tari_amount.draw(f, v_chunks[2], state);

self.button.draw(f, v_chunks[4], state);
self.password.draw(f, v_chunks[4], state);
self.button.draw(f, v_chunks[5], state);
}
}
Loading

0 comments on commit 038a9bb

Please sign in to comment.