[Add] Graph Cards, Some UI fixes [TODO] Manual closing after Agreements
screens is neccesarry
This commit is contained in:
parent
997c567c80
commit
0669b8be0a
13 changed files with 206 additions and 107 deletions
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::gui::elements::log_card::UiLogEntry;
|
use crate::{ACTIVE_TASKS, APP_STATE, SHUTDOWN, gui::elements::log_card::UiLogEntry};
|
||||||
use json::{JsonValue, object};
|
use json::{JsonValue, object};
|
||||||
use std::collections::VecDeque;
|
use std::{collections::VecDeque, thread, time::Duration};
|
||||||
|
use sysinfo::{RefreshKind, System};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
|
|
@ -138,3 +139,63 @@ impl AppState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn setup() {
|
||||||
|
ACTIVE_TASKS.insert("System info loader".to_string());
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let mut sys = System::new_with_specifics(RefreshKind::new());
|
||||||
|
let mut last_total_received = 0u64;
|
||||||
|
let mut last_total_transmitted = 0u64;
|
||||||
|
let mut counter = 0.0;
|
||||||
|
loop {
|
||||||
|
if *SHUTDOWN.read().await {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
sys.refresh_all();
|
||||||
|
|
||||||
|
let mut tcpu = 0;
|
||||||
|
for cpu in sys.cpus() {
|
||||||
|
tcpu += cpu.cpu_usage() as i64;
|
||||||
|
tcpu /= 2;
|
||||||
|
}
|
||||||
|
let ram = (sys.used_memory() as f64 / sys.total_memory() as f64) * 100.0;
|
||||||
|
|
||||||
|
let total_received = 0u64;
|
||||||
|
let total_transmitted = 0u64;
|
||||||
|
|
||||||
|
let delta_received = if last_total_received == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
total_received.saturating_sub(last_total_received)
|
||||||
|
};
|
||||||
|
let delta_transmitted = if last_total_transmitted == 0 {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
total_transmitted.saturating_sub(last_total_transmitted)
|
||||||
|
};
|
||||||
|
last_total_received = total_received;
|
||||||
|
last_total_transmitted = total_transmitted;
|
||||||
|
|
||||||
|
let net_down = delta_received as f64;
|
||||||
|
let net_up = delta_transmitted as f64;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut st = APP_STATE.lock().unwrap();
|
||||||
|
st.push_cpu((counter, tcpu as f64));
|
||||||
|
st.push_ram((counter, ram));
|
||||||
|
st.push_net_down((counter, net_down));
|
||||||
|
st.push_net_up((counter, net_up));
|
||||||
|
|
||||||
|
st.sys_info = format!("NetDown: {} NetUp: {}", delta_received, delta_transmitted);
|
||||||
|
}
|
||||||
|
|
||||||
|
counter += 1.0;
|
||||||
|
if counter > 30.0 {
|
||||||
|
thread::sleep(Duration::from_millis(500));
|
||||||
|
} else {
|
||||||
|
thread::sleep(Duration::from_millis(5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ACTIVE_TASKS.remove("System info loader");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -124,8 +124,14 @@ impl InteractableElement for ConsoleCard {
|
||||||
fn interact(&mut self, key: KeyEvent) -> InteractionResult {
|
fn interact(&mut self, key: KeyEvent) -> InteractionResult {
|
||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Enter => {
|
KeyCode::Enter => {
|
||||||
|
if self.content.is_empty() {
|
||||||
|
log!("");
|
||||||
|
return InteractionResult::Handled;
|
||||||
|
}
|
||||||
let command = self.content.clone();
|
let command = self.content.clone();
|
||||||
let id = Uuid::new_v4();
|
let id = Uuid::new_v4();
|
||||||
|
let id = id.to_string();
|
||||||
|
let id = id.split_at(8).0;
|
||||||
let task_id = format!("command_{}_{}", command, id);
|
let task_id = format!("command_{}_{}", command, id);
|
||||||
ACTIVE_TASKS.insert(task_id.clone());
|
ACTIVE_TASKS.insert(task_id.clone());
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,15 @@ use ratatui::{Frame, layout::Rect, widgets::Borders};
|
||||||
|
|
||||||
use crate::gui::{interaction_result::InteractionResult, screens::screens::Screen};
|
use crate::gui::{interaction_result::InteractionResult, screens::screens::Screen};
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub trait Element: Send + Sync + Any {
|
pub trait Element: Send + Sync + Any {
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||||
|
|
||||||
fn render(&self, f: &mut Frame, r: Rect);
|
fn render(&self, f: &mut Frame, r: Rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub trait JoinableElement: Send + Sync + Any {
|
pub trait JoinableElement: Send + Sync + Any {
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||||
|
|
@ -21,6 +24,7 @@ pub trait JoinableElement: Send + Sync + Any {
|
||||||
fn set_joins(&mut self, joins: Borders);
|
fn set_joins(&mut self, joins: Borders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub trait InfoElement: Send + Sync + Any {
|
pub trait InfoElement: Send + Sync + Any {
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||||
|
|
@ -30,6 +34,7 @@ pub trait InfoElement: Send + Sync + Any {
|
||||||
fn get_info_screen(&self) -> Box<dyn Screen>;
|
fn get_info_screen(&self) -> Box<dyn Screen>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub trait InteractableElement: Send + Sync + Any {
|
pub trait InteractableElement: Send + Sync + Any {
|
||||||
fn as_any(&self) -> &dyn Any;
|
fn as_any(&self) -> &dyn Any;
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||||
|
|
|
||||||
|
|
@ -28,11 +28,19 @@ pub enum GRAPHS {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GRAPHS {
|
impl GRAPHS {
|
||||||
|
pub fn get_color(&self) -> Color {
|
||||||
|
match self {
|
||||||
|
GRAPHS::Ram => Color::Blue,
|
||||||
|
GRAPHS::Cpu => Color::Red,
|
||||||
|
GRAPHS::Ping => Color::Green,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_graph(&self) -> Vec<(f64, f64)> {
|
pub fn get_graph(&self) -> Vec<(f64, f64)> {
|
||||||
match self {
|
match self {
|
||||||
GRAPHS::Ram => APP_STATE.lock().unwrap().ram.clone(),
|
GRAPHS::Ram => APP_STATE.lock().unwrap().with_width(28).ram.clone(),
|
||||||
GRAPHS::Cpu => APP_STATE.lock().unwrap().cpu.clone(),
|
GRAPHS::Cpu => APP_STATE.lock().unwrap().with_width(28).cpu.clone(),
|
||||||
GRAPHS::Ping => APP_STATE.lock().unwrap().ping.clone(),
|
GRAPHS::Ping => APP_STATE.lock().unwrap().with_width(28).ping.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,13 +53,13 @@ impl GRAPHS {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub struct GraphCard {
|
pub struct GraphCard {
|
||||||
ui: Arc<UI>,
|
ui: Arc<UI>,
|
||||||
graph_type: GRAPHS,
|
graph_type: GRAPHS,
|
||||||
|
|
||||||
focused: bool,
|
focused: bool,
|
||||||
pub title: String,
|
pub title: String,
|
||||||
color: Color,
|
|
||||||
|
|
||||||
borders: Borders,
|
borders: Borders,
|
||||||
joins: Borders,
|
joins: Borders,
|
||||||
|
|
@ -66,10 +74,9 @@ impl GraphCard {
|
||||||
graph_type,
|
graph_type,
|
||||||
focused: false,
|
focused: false,
|
||||||
title,
|
title,
|
||||||
color: Color::White,
|
|
||||||
borders: Borders::ALL,
|
borders: Borders::ALL,
|
||||||
joins: Borders::NONE,
|
joins: Borders::NONE,
|
||||||
open: false,
|
open: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -98,11 +105,11 @@ impl Element for GraphCard {
|
||||||
.filter(|y| *y > 0.0)
|
.filter(|y| *y > 0.0)
|
||||||
.min_by(|a, b| a.total_cmp(b))
|
.min_by(|a, b| a.total_cmp(b))
|
||||||
.unwrap_or(0.0);
|
.unwrap_or(0.0);
|
||||||
let max_y = graph.iter().map(|(_, y)| *y).fold(f64::MIN, f64::max);
|
let max_y = graph.iter().map(|(_, y)| *y).fold(-1.0, f64::max);
|
||||||
|
|
||||||
let block = Block::default()
|
let block = Block::default()
|
||||||
.title(format!(
|
.title(format!(
|
||||||
"─{}:─{}{}──{}/{}─MIN/MAX",
|
"{}:─{}{}─{}min/{}max",
|
||||||
self.title,
|
self.title,
|
||||||
graph.last().unwrap_or(&(0.0, 0.0)).1 as i64,
|
graph.last().unwrap_or(&(0.0, 0.0)).1 as i64,
|
||||||
unit,
|
unit,
|
||||||
|
|
@ -127,7 +134,7 @@ impl Element for GraphCard {
|
||||||
y1: 0.0,
|
y1: 0.0,
|
||||||
x2: *x,
|
x2: *x,
|
||||||
y2: *y,
|
y2: *y,
|
||||||
color: self.color,
|
color: self.graph_type.get_color(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ pub mod elements {
|
||||||
}
|
}
|
||||||
pub mod screens {
|
pub mod screens {
|
||||||
pub mod main_screen;
|
pub mod main_screen;
|
||||||
|
pub mod md_viewer;
|
||||||
pub mod screens;
|
pub mod screens;
|
||||||
pub mod terms_checker;
|
pub mod terms_checker;
|
||||||
pub mod terms_updater;
|
pub mod terms_updater;
|
||||||
|
|
|
||||||
|
|
@ -32,8 +32,8 @@ impl MainScreen {
|
||||||
|
|
||||||
let nav_grid = vec![
|
let nav_grid = vec![
|
||||||
vec![Some(0), Some(2)],
|
vec![Some(0), Some(2)],
|
||||||
vec![Some(1), Some(3)],
|
vec![Some(0), Some(3)],
|
||||||
vec![None, Some(4)],
|
vec![Some(1), Some(4)],
|
||||||
];
|
];
|
||||||
|
|
||||||
let mut log_card = LogCard::new();
|
let mut log_card = LogCard::new();
|
||||||
|
|
@ -44,10 +44,10 @@ impl MainScreen {
|
||||||
elements.push(Box::new(log_card));
|
elements.push(Box::new(log_card));
|
||||||
elements.push(Box::new(console_card));
|
elements.push(Box::new(console_card));
|
||||||
|
|
||||||
let mut ram_graph = GraphCard::new(ui.clone(), GRAPHS::Ram, "RAM Usage".into());
|
let mut ram_graph = GraphCard::new(ui.clone(), GRAPHS::Ram, "RAM".into());
|
||||||
ram_graph.set_borders(Borders::TOP.union(Borders::LEFT).union(Borders::RIGHT));
|
ram_graph.set_borders(Borders::TOP.union(Borders::LEFT).union(Borders::RIGHT));
|
||||||
elements.push(Box::new(ram_graph));
|
elements.push(Box::new(ram_graph));
|
||||||
let mut cpu_graph = GraphCard::new(ui.clone(), GRAPHS::Cpu, "CPU Usage".into());
|
let mut cpu_graph = GraphCard::new(ui.clone(), GRAPHS::Cpu, "CPU".into());
|
||||||
cpu_graph.set_borders(Borders::TOP.union(Borders::LEFT).union(Borders::RIGHT));
|
cpu_graph.set_borders(Borders::TOP.union(Borders::LEFT).union(Borders::RIGHT));
|
||||||
cpu_graph.set_joins(Borders::TOP);
|
cpu_graph.set_joins(Borders::TOP);
|
||||||
elements.push(Box::new(cpu_graph));
|
elements.push(Box::new(cpu_graph));
|
||||||
|
|
@ -87,52 +87,65 @@ impl MainScreen {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn navigate(&mut self, direction: NavDirection) {
|
fn navigate(&mut self, direction: NavDirection) {
|
||||||
let (mut y, mut x) = self.selected_coords;
|
let (current_row, current_col) = self.selected_coords;
|
||||||
|
let current_element = self.nav_grid[current_row][current_col];
|
||||||
|
|
||||||
self.unfocus_current(y, x);
|
self.unfocus_current(current_row, current_col);
|
||||||
|
|
||||||
match direction {
|
let (delta_row, delta_col) = match direction {
|
||||||
NavDirection::Up => {
|
NavDirection::Up => (-1isize, 0),
|
||||||
if y > 0 {
|
NavDirection::Down => (1, 0),
|
||||||
y -= 1;
|
NavDirection::Left => (0, -1),
|
||||||
|
NavDirection::Right => (0, 1),
|
||||||
|
_ => (0, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut next_row = current_row as isize;
|
||||||
|
let mut next_col = current_col as isize;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
next_row += delta_row;
|
||||||
|
next_col += delta_col;
|
||||||
|
|
||||||
|
if next_row < 0 || next_col < 0 {
|
||||||
|
self.selected_coords = (
|
||||||
|
(next_row - delta_row) as usize,
|
||||||
|
(next_col - delta_col) as usize,
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
let next_row_u = next_row as usize;
|
||||||
NavDirection::Down => {
|
let next_col_u = next_col as usize;
|
||||||
if y < self.nav_grid.len() - 1 {
|
|
||||||
y += 1;
|
if next_row_u >= self.nav_grid.len() {
|
||||||
}
|
self.selected_coords = (
|
||||||
}
|
(next_row - delta_row) as usize,
|
||||||
NavDirection::Left => {
|
(next_col - delta_col) as usize,
|
||||||
if x > 0 {
|
);
|
||||||
x -= 1;
|
break;
|
||||||
}
|
|
||||||
}
|
|
||||||
NavDirection::Right => {
|
|
||||||
if let Some(row) = self.nav_grid.get(y) {
|
|
||||||
if x < row.len() - 1 {
|
|
||||||
x += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(row) = self.nav_grid.get(y) {
|
if let Some(row) = self.nav_grid.get(next_row_u) {
|
||||||
if x >= row.len() {
|
if next_col_u >= row.len() {
|
||||||
x = row.len() - 1;
|
self.selected_coords = (
|
||||||
}
|
(next_row - delta_row) as usize,
|
||||||
|
(next_col - delta_col) as usize,
|
||||||
|
);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self
|
if let Some(next_element) = row[next_col_u] {
|
||||||
.nav_grid
|
if Some(next_element) != current_element {
|
||||||
.get(y)
|
self.selected_coords = (next_row_u, next_col_u);
|
||||||
.and_then(|r| r.get(x))
|
|
||||||
.map_or(false, |e| e.is_some())
|
|
||||||
{
|
|
||||||
self.selected_coords = (y, x);
|
|
||||||
self.focus_current();
|
self.focus_current();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.focus_current();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Screen for MainScreen {
|
impl Screen for MainScreen {
|
||||||
|
|
@ -156,21 +169,26 @@ impl Screen for MainScreen {
|
||||||
let graphs_width = if self.graphs_open { 30 } else { 2 };
|
let graphs_width = if self.graphs_open { 30 } else { 2 };
|
||||||
let main_width = inner.width.saturating_sub(graphs_width);
|
let main_width = inner.width.saturating_sub(graphs_width);
|
||||||
|
|
||||||
let chunks = Layout::default()
|
let horizontal_chunks = Layout::default()
|
||||||
.direction(ratatui::layout::Direction::Horizontal)
|
.direction(ratatui::layout::Direction::Horizontal)
|
||||||
.constraints([
|
.constraints([
|
||||||
Constraint::Length(main_width),
|
Constraint::Length(main_width),
|
||||||
Constraint::Length(graphs_width),
|
Constraint::Length(graphs_width),
|
||||||
])
|
])
|
||||||
.split(inner);
|
.split(inner);
|
||||||
let left_chunks =
|
|
||||||
Layout::vertical([Constraint::Min(0), Constraint::Length(3)]).split(chunks[0]);
|
|
||||||
|
|
||||||
if let Some(Some(index)) = self.nav_grid.get(0).and_then(|r| r.get(0)) {
|
let left_area = horizontal_chunks[0];
|
||||||
self.elements[*index].as_element().render(f, left_chunks[0]);
|
let right_area = horizontal_chunks[1];
|
||||||
|
|
||||||
|
let left_rows =
|
||||||
|
Layout::vertical([Constraint::Min(0), Constraint::Length(3)]).split(left_area);
|
||||||
|
|
||||||
|
if let Some(log) = self.elements.get(0) {
|
||||||
|
log.as_element().render(f, left_rows[0]);
|
||||||
}
|
}
|
||||||
if let Some(Some(index)) = self.nav_grid.get(1).and_then(|r| r.get(0)) {
|
|
||||||
self.elements[*index].as_element().render(f, left_chunks[1]);
|
if let Some(console) = self.elements.get(1) {
|
||||||
|
console.as_element().render(f, left_rows[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
let graph_elements: Vec<_> = self
|
let graph_elements: Vec<_> = self
|
||||||
|
|
@ -179,18 +197,20 @@ impl Screen for MainScreen {
|
||||||
.filter(|el| el.as_any().is::<GraphCard>())
|
.filter(|el| el.as_any().is::<GraphCard>())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
if !graph_elements.is_empty() {
|
||||||
let graph_chunks = Layout::vertical(
|
let graph_chunks = Layout::vertical(
|
||||||
graph_elements
|
graph_elements
|
||||||
.iter()
|
.iter()
|
||||||
.map(|_| Constraint::Length(chunks[1].height / graph_elements.len() as u16))
|
.map(|_| Constraint::Ratio(1, graph_elements.len() as u32))
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
)
|
)
|
||||||
.split(chunks[1]);
|
.split(right_area);
|
||||||
|
|
||||||
for (el, area) in graph_elements.iter().zip(graph_chunks.iter()) {
|
for (el, area) in graph_elements.iter().zip(graph_chunks.iter()) {
|
||||||
el.as_element().render(f, *area);
|
el.as_element().render(f, *area);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_input(&mut self, event: KeyEvent) -> InteractionResult {
|
fn handle_input(&mut self, event: KeyEvent) -> InteractionResult {
|
||||||
match event.code {
|
match event.code {
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,11 @@ use ratatui::{
|
||||||
text::{Line, Span},
|
text::{Line, Span},
|
||||||
widgets::{Block, Borders, Paragraph, Wrap},
|
widgets::{Block, Borders, Paragraph, Wrap},
|
||||||
};
|
};
|
||||||
use std::{any::Any, sync::Arc, time::Duration};
|
use std::{any::Any, time::Duration};
|
||||||
|
|
||||||
use crate::gui::{interaction_result::InteractionResult, screens::screens::Screen, ui::UI};
|
use crate::gui::{interaction_result::InteractionResult, screens::screens::Screen};
|
||||||
|
|
||||||
pub struct FileViewer {
|
pub struct FileViewer {
|
||||||
ui: Arc<UI>,
|
|
||||||
title: String,
|
title: String,
|
||||||
text: Vec<DisplayLine>,
|
text: Vec<DisplayLine>,
|
||||||
scroll: u16,
|
scroll: u16,
|
||||||
|
|
@ -25,10 +24,6 @@ impl Screen for FileViewer {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_ui(&self) -> &Arc<UI> {
|
|
||||||
&self.ui
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&self, f: &mut Frame, rect: Rect) {
|
fn render(&self, f: &mut Frame, rect: Rect) {
|
||||||
self.draw(f, rect);
|
self.draw(f, rect);
|
||||||
}
|
}
|
||||||
|
|
@ -54,9 +49,8 @@ impl Screen for FileViewer {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FileViewer {
|
impl FileViewer {
|
||||||
pub fn new(ui: Arc<UI>, title: String, content: &str) -> Self {
|
pub fn new(title: String, content: &str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
ui,
|
|
||||||
title,
|
title,
|
||||||
text: parse_document(content.to_owned()),
|
text: parse_document(content.to_owned()),
|
||||||
scroll: 0,
|
scroll: 0,
|
||||||
|
|
@ -1,10 +1,13 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
gui::{interaction_result::InteractionResult, screens::screens::Screen, ui::UI},
|
gui::{
|
||||||
|
interaction_result::InteractionResult,
|
||||||
|
screens::{md_viewer::FileViewer, screens::Screen},
|
||||||
|
ui::UI,
|
||||||
|
},
|
||||||
terms::{
|
terms::{
|
||||||
buttons::{checkbox, draw_buttons},
|
buttons::{checkbox, draw_buttons},
|
||||||
consent_state::UserChoice,
|
consent_state::UserChoice,
|
||||||
focus::Focus,
|
focus::Focus,
|
||||||
md_viewer::FileViewer,
|
|
||||||
terms_getter::{Type, get_link, get_terms},
|
terms_getter::{Type, get_link, get_terms},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
@ -279,12 +282,11 @@ impl Screen for TermsCheckerScreen {
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
if let Some(terms_type) = terms_type {
|
if let Some(terms_type) = terms_type {
|
||||||
let ui = self.ui.clone();
|
|
||||||
let fut: Pin<Box<dyn Future<Output = Box<dyn Screen>> + Send>> =
|
let fut: Pin<Box<dyn Future<Output = Box<dyn Screen>> + Send>> =
|
||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
let content = get_terms(terms_type.clone()).await.unwrap();
|
let content = get_terms(terms_type.clone()).await.unwrap();
|
||||||
let screen: FileViewer =
|
let screen: FileViewer =
|
||||||
FileViewer::new(ui.clone(), terms_type.to_string(), &content);
|
FileViewer::new(terms_type.to_string(), &content);
|
||||||
Box::new(screen) as Box<dyn Screen>
|
Box::new(screen) as Box<dyn Screen>
|
||||||
});
|
});
|
||||||
InteractionResult::OpenFutureScreen { screen: fut }
|
InteractionResult::OpenFutureScreen { screen: fut }
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
gui::{interaction_result::InteractionResult, screens::screens::Screen, ui::UI},
|
gui::{
|
||||||
|
interaction_result::InteractionResult,
|
||||||
|
screens::{md_viewer::FileViewer, screens::Screen},
|
||||||
|
},
|
||||||
terms::{
|
terms::{
|
||||||
buttons::{checkbox, draw_buttons},
|
buttons::{checkbox, draw_buttons},
|
||||||
consent_state::{UpdateDecision, UserChoice},
|
consent_state::{UpdateDecision, UserChoice},
|
||||||
doc::Doc,
|
doc::Doc,
|
||||||
focus::Focus,
|
focus::Focus,
|
||||||
md_viewer::FileViewer,
|
terms_getter::{Type, get_newest_link, get_terms},
|
||||||
terms_getter::{Type, get_link, get_terms},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use chrono::{Local, TimeZone, Utc};
|
use chrono::{Local, TimeZone, Utc};
|
||||||
|
|
@ -19,11 +21,9 @@ use ratatui::{
|
||||||
widgets::{Block, Borders, Paragraph},
|
widgets::{Block, Borders, Paragraph},
|
||||||
};
|
};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::sync::Arc;
|
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
pub struct TermsUpdaterScreen {
|
pub struct TermsUpdaterScreen {
|
||||||
ui: Arc<UI>,
|
|
||||||
sender: Option<oneshot::Sender<UserChoice>>,
|
sender: Option<oneshot::Sender<UserChoice>>,
|
||||||
|
|
||||||
eula_needed: bool,
|
eula_needed: bool,
|
||||||
|
|
@ -48,7 +48,6 @@ pub struct TermsUpdaterScreen {
|
||||||
}
|
}
|
||||||
impl TermsUpdaterScreen {
|
impl TermsUpdaterScreen {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
ui: Arc<UI>,
|
|
||||||
consent_eula: UpdateDecision,
|
consent_eula: UpdateDecision,
|
||||||
consent_tos: UpdateDecision,
|
consent_tos: UpdateDecision,
|
||||||
consent_pp: UpdateDecision,
|
consent_pp: UpdateDecision,
|
||||||
|
|
@ -87,7 +86,6 @@ impl TermsUpdaterScreen {
|
||||||
|| (pp_needed && !pp_future);
|
|| (pp_needed && !pp_future);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
ui,
|
|
||||||
sender,
|
sender,
|
||||||
|
|
||||||
eula_needed,
|
eula_needed,
|
||||||
|
|
@ -653,14 +651,10 @@ impl Screen for TermsUpdaterScreen {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(terms_type) = terms_type {
|
if let Some(terms_type) = terms_type {
|
||||||
let ui = self.ui.clone();
|
|
||||||
let fut = Box::pin(async move {
|
let fut = Box::pin(async move {
|
||||||
let content = get_terms(terms_type.clone()).await.unwrap();
|
let content = get_terms(terms_type.clone()).await.unwrap();
|
||||||
Box::new(FileViewer::new(
|
Box::new(FileViewer::new(terms_type.to_string(), &content))
|
||||||
ui.clone(),
|
as Box<dyn Screen>
|
||||||
terms_type.to_string(),
|
|
||||||
&content,
|
|
||||||
)) as Box<dyn Screen>
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return InteractionResult::OpenFutureScreen { screen: fut };
|
return InteractionResult::OpenFutureScreen { screen: fut };
|
||||||
|
|
@ -670,15 +664,15 @@ impl Screen for TermsUpdaterScreen {
|
||||||
}
|
}
|
||||||
KeyCode::Char('l') | KeyCode::Char('L') => match self.focus {
|
KeyCode::Char('l') | KeyCode::Char('L') => match self.focus {
|
||||||
Focus::Eula => {
|
Focus::Eula => {
|
||||||
let _ = open::that(get_link(Type::EULA));
|
let _ = open::that(get_newest_link(Type::EULA));
|
||||||
InteractionResult::Handled
|
InteractionResult::Handled
|
||||||
}
|
}
|
||||||
Focus::Tos => {
|
Focus::Tos => {
|
||||||
let _ = open::that(get_link(Type::TOS));
|
let _ = open::that(get_newest_link(Type::TOS));
|
||||||
InteractionResult::Handled
|
InteractionResult::Handled
|
||||||
}
|
}
|
||||||
Focus::Pp => {
|
Focus::Pp => {
|
||||||
let _ = open::that(get_link(Type::PP));
|
let _ = open::that(get_newest_link(Type::PP));
|
||||||
InteractionResult::Handled
|
InteractionResult::Handled
|
||||||
}
|
}
|
||||||
_ => InteractionResult::Unhandled,
|
_ => InteractionResult::Unhandled,
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ pub static FPS: Lazy<RwLock<(f64, f64)>> = Lazy::new(|| RwLock::new((0.0, 0.0)))
|
||||||
|
|
||||||
pub struct UI {
|
pub struct UI {
|
||||||
pub terminal: Arc<Mutex<Terminal<CrosstermBackend<Stdout>>>>,
|
pub terminal: Arc<Mutex<Terminal<CrosstermBackend<Stdout>>>>,
|
||||||
screen: Arc<RwLock<Option<Box<dyn Screen>>>>,
|
screen_stack: Arc<RwLock<Vec<Box<dyn Screen>>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_tui() -> Arc<UI> {
|
pub fn start_tui() -> Arc<UI> {
|
||||||
|
|
@ -111,18 +111,22 @@ impl UI {
|
||||||
let terminal = init();
|
let terminal = init();
|
||||||
Self {
|
Self {
|
||||||
terminal: Arc::new(Mutex::new(terminal)),
|
terminal: Arc::new(Mutex::new(terminal)),
|
||||||
screen: Arc::new(RwLock::new(None)),
|
screen_stack: Arc::new(RwLock::new(Vec::new())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn set_screen(&self, screen: Box<dyn Screen>) {
|
pub async fn set_screen(&self, screen: Box<dyn Screen>) {
|
||||||
*self.screen.write().await = Some(screen);
|
self.screen_stack.write().await.push(screen);
|
||||||
|
}
|
||||||
|
pub async fn replace_screen(&self, screen: Box<dyn Screen>) {
|
||||||
|
let mut stack = self.screen_stack.write().await;
|
||||||
|
stack.pop();
|
||||||
|
stack.push(screen);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn handle_input(self: Arc<Self>, key_event: KeyEvent) {
|
pub async fn handle_input(self: Arc<Self>, key_event: KeyEvent) {
|
||||||
let result = {
|
let result = {
|
||||||
let mut guard = self.screen.write().await;
|
let mut stack = self.screen_stack.write().await;
|
||||||
if let Some(screen) = guard.as_mut() {
|
if let Some(screen) = stack.last_mut() {
|
||||||
screen.handle_input(key_event)
|
screen.handle_input(key_event)
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
|
|
@ -138,7 +142,12 @@ impl UI {
|
||||||
ui.set_screen(screen).await;
|
ui.set_screen(screen).await;
|
||||||
}
|
}
|
||||||
InteractionResult::CloseScreen => {
|
InteractionResult::CloseScreen => {
|
||||||
*self.screen.write().await = None;
|
let mut stack = self.screen_stack.write().await;
|
||||||
|
stack.pop();
|
||||||
|
|
||||||
|
if stack.is_empty() {
|
||||||
|
*SHUTDOWN.write().await = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
InteractionResult::Handled => {}
|
InteractionResult::Handled => {}
|
||||||
InteractionResult::Unhandled => {}
|
InteractionResult::Unhandled => {}
|
||||||
|
|
@ -146,7 +155,7 @@ impl UI {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn render(&self) {
|
pub async fn render(&self) {
|
||||||
if let Some(screen) = self.screen.read().await.as_ref() {
|
if let Some(screen) = self.screen_stack.read().await.last() {
|
||||||
let mut terminal = self.terminal.lock().unwrap();
|
let mut terminal = self.terminal.lock().unwrap();
|
||||||
terminal
|
terminal
|
||||||
.draw(|f| {
|
.draw(|f| {
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ mod util;
|
||||||
|
|
||||||
use crate::communities::community_manager;
|
use crate::communities::community_manager;
|
||||||
use crate::communities::interactables::registry;
|
use crate::communities::interactables::registry;
|
||||||
|
use crate::gui::app_state;
|
||||||
use crate::gui::app_state::AppState;
|
use crate::gui::app_state::AppState;
|
||||||
use crate::gui::screens::main_screen::MainScreen;
|
use crate::gui::screens::main_screen::MainScreen;
|
||||||
use crate::gui::ui::start_tui;
|
use crate::gui::ui::start_tui;
|
||||||
|
|
@ -78,6 +79,7 @@ async fn main() {
|
||||||
println!("You can find this at 'agreements'!");
|
println!("You can find this at 'agreements'!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
app_state::setup();
|
||||||
|
|
||||||
let main_screen = MainScreen::new(ui.clone()).await;
|
let main_screen = MainScreen::new(ui.clone()).await;
|
||||||
ui.set_screen(Box::new(main_screen)).await;
|
ui.set_screen(Box::new(main_screen)).await;
|
||||||
|
|
|
||||||
|
|
@ -59,7 +59,7 @@ async fn ensure_initial_consent(ui: Arc<UI>, state: &mut ConsentState) -> Result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&state.save_state();
|
let _ = &state.save_state();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
UserChoice::Deny => Err(()),
|
UserChoice::Deny => Err(()),
|
||||||
|
|
@ -77,7 +77,6 @@ async fn ensure_updates(ui: Arc<UI>, state: &mut ConsentState) -> Result<(), ()>
|
||||||
let (tx, rx) = oneshot::channel();
|
let (tx, rx) = oneshot::channel();
|
||||||
|
|
||||||
ui.set_screen(Box::new(TermsUpdaterScreen::new(
|
ui.set_screen(Box::new(TermsUpdaterScreen::new(
|
||||||
ui.clone(),
|
|
||||||
eula_update.clone(),
|
eula_update.clone(),
|
||||||
tos_update.clone(),
|
tos_update.clone(),
|
||||||
privacy_update.clone(),
|
privacy_update.clone(),
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,4 @@ pub mod buttons;
|
||||||
pub mod consent_state;
|
pub mod consent_state;
|
||||||
pub mod doc;
|
pub mod doc;
|
||||||
pub mod focus;
|
pub mod focus;
|
||||||
pub mod md_viewer;
|
|
||||||
pub mod terms_getter;
|
pub mod terms_getter;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue