Almost kinda functional description box

This commit is contained in:
2023-08-08 10:50:04 -06:00
parent d39acb0683
commit b24e0cdccd
16 changed files with 710 additions and 288 deletions
+23 -23
View File
@@ -17,29 +17,29 @@ generate_keybindings! {
}
pub struct KeyBinding {
pub key: Key,
pub desc: &'static str
pub key: Key,
pub desc: &'static str,
}
pub const DEFAULT_KEYBINDINGS: KeyBindings = KeyBindings {
quit: KeyBinding {
key: Key::Char('q'),
desc: "Quit",
},
up: KeyBinding {
key: Key::Up,
desc: "Scroll up"
},
down: KeyBinding {
key: Key::Down,
desc: "Scroll down"
},
submit: KeyBinding {
key: Key::Enter,
desc: "Select"
},
esc: KeyBinding {
key: Key::Esc,
desc: "Exit menu"
}
};
quit: KeyBinding {
key: Key::Char('q'),
desc: "Quit",
},
up: KeyBinding {
key: Key::Up,
desc: "Scroll up",
},
down: KeyBinding {
key: Key::Down,
desc: "Scroll down",
},
submit: KeyBinding {
key: Key::Enter,
desc: "Select",
},
esc: KeyBinding {
key: Key::Esc,
desc: "Exit current menu",
},
};
+15 -68
View File
@@ -1,21 +1,27 @@
use log::error;
use log::{debug, error};
use reqwest::Client;
use serde::{Deserialize, Serialize};
use tokio::sync::mpsc::Sender;
use tui::widgets::TableState;
use crate::app::radarr::{ActiveRadarrBlock, RadarrData};
use crate::network::NetworkEvent;
use crate::network::radarr_network::RadarrEvent;
use crate::network::NetworkEvent;
pub(crate) mod key_binding;
pub mod models;
pub mod radarr;
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum Route {
Radarr(ActiveRadarrBlock),
}
impl From<ActiveRadarrBlock> for Route {
fn from(active_radarr_block: ActiveRadarrBlock) -> Route {
Route::Radarr(active_radarr_block)
}
}
const DEFAULT_ROUTE: Route = Route::Radarr(ActiveRadarrBlock::Movies);
pub struct App {
@@ -26,6 +32,7 @@ pub struct App {
pub tick_until_poll: u64,
pub tick_count: u64,
pub is_routing: bool,
pub is_loading: bool,
pub config: AppConfig,
pub data: Data,
}
@@ -41,8 +48,10 @@ impl App {
}
pub async fn dispatch(&mut self, action: NetworkEvent) {
self.is_loading = true;
if let Some(network_tx) = &self.network_tx {
if let Err(e) = network_tx.send(action).await {
self.is_loading = false;
error!("Failed to send event. {:?}", e);
}
}
@@ -70,7 +79,6 @@ impl App {
self.dispatch(RadarrEvent::GetOverview.into()).await;
self.dispatch(RadarrEvent::GetStatus.into()).await;
self.dispatch_by_radarr_block(active_block).await;
}
}
@@ -87,6 +95,7 @@ impl App {
}
pub fn pop_navigation_stack(&mut self) {
self.is_routing = true;
if self.navigation_stack.len() > 1 {
self.navigation_stack.pop();
}
@@ -106,6 +115,7 @@ impl Default for App {
title: "Managarr",
tick_until_poll: 0,
tick_count: 0,
is_loading: false,
is_routing: false,
config: AppConfig::default(),
data: Data::default(),
@@ -139,66 +149,3 @@ impl Default for RadarrConfig {
}
}
}
pub struct StatefulTable<T> {
pub state: TableState,
pub items: Vec<T>
}
impl<T> Default for StatefulTable<T> {
fn default() -> StatefulTable<T> {
StatefulTable {
state: TableState::default(),
items: Vec::new()
}
}
}
impl<T> StatefulTable<T> {
pub fn set_items(&mut self, items: Vec<T>) {
let items_len = items.len();
self.items = items;
if !self.items.is_empty() {
let selected_row = self.state.selected().map_or(0, |i| {
if i > 0 && i < items_len {
i
} else if i >= items_len {
items_len - 1
} else {
0
}
});
self.state.select(Some(selected_row));
}
}
pub fn scroll_down(&mut self) {
let selected_row = match self.state.selected() {
Some(i) => {
if i >= self.items.len() - 1 {
0
} else {
i + 1
}
}
None => 0
};
self.state.select(Some(selected_row));
}
pub fn scroll_up(&mut self) {
let selected_row = match self.state.selected() {
Some(i) => {
if i == 0 {
self.items.len() - 1
} else {
i - 1
}
}
None => 0
};
self.state.select(Some(selected_row));
}
}
+107
View File
@@ -0,0 +1,107 @@
use tui::widgets::TableState;
pub trait Scrollable {
fn scroll_down(&mut self);
fn scroll_up(&mut self);
}
pub struct StatefulTable<T> {
pub state: TableState,
pub items: Vec<T>,
}
impl<T> Default for StatefulTable<T> {
fn default() -> StatefulTable<T> {
StatefulTable {
state: TableState::default(),
items: Vec::new(),
}
}
}
impl<T> StatefulTable<T> {
pub fn set_items(&mut self, items: Vec<T>) {
let items_len = items.len();
self.items = items;
if !self.items.is_empty() {
let selected_row = self.state.selected().map_or(0, |i| {
if i > 0 && i < items_len {
i
} else if i >= items_len {
items_len - 1
} else {
0
}
});
self.state.select(Some(selected_row));
}
}
pub fn current_selection(&self) -> &T {
&self.items[self.state.selected().unwrap_or(0)]
}
}
impl<T> Scrollable for StatefulTable<T> {
fn scroll_down(&mut self) {
let selected_row = match self.state.selected() {
Some(i) => {
if i >= self.items.len() - 1 {
0
} else {
i + 1
}
}
None => 0,
};
self.state.select(Some(selected_row));
}
fn scroll_up(&mut self) {
let selected_row = match self.state.selected() {
Some(i) => {
if i == 0 {
self.items.len() - 1
} else {
i - 1
}
}
None => 0,
};
self.state.select(Some(selected_row));
}
}
#[derive(Default)]
pub struct ScrollableText {
pub items: Vec<String>,
pub offset: u16,
}
impl ScrollableText {
pub fn with_string(item: String) -> ScrollableText {
let items: Vec<&str> = item.split('\n').collect();
let items: Vec<String> = items.iter().map(|it| it.to_string()).collect();
ScrollableText { items, offset: 0 }
}
pub fn get_text(&self) -> String {
self.items.join("\n")
}
}
impl Scrollable for ScrollableText {
fn scroll_down(&mut self) {
if self.offset < self.items.len() as u16 {
self.offset += 1;
}
}
fn scroll_up(&mut self) {
if self.offset > 0 {
self.offset -= 1;
}
}
}
+8 -4
View File
@@ -2,7 +2,8 @@ use std::collections::HashMap;
use chrono::{DateTime, Utc};
use crate::app::{App, StatefulTable};
use crate::app::models::{ScrollableText, StatefulTable};
use crate::app::App;
use crate::network::radarr_network::{DownloadRecord, Movie, RadarrEvent};
#[derive(Default)]
@@ -14,9 +15,10 @@ pub struct RadarrData {
pub movies: StatefulTable<Movie>,
pub downloads: StatefulTable<DownloadRecord>,
pub quality_profile_map: HashMap<u64, String>,
pub movie_details: ScrollableText,
}
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ActiveRadarrBlock {
AddMovie,
Calendar,
@@ -33,13 +35,15 @@ pub enum ActiveRadarrBlock {
impl App {
pub(super) async fn dispatch_by_radarr_block(&mut self, active_radarr_block: ActiveRadarrBlock) {
self.reset_tick_count();
match active_radarr_block {
ActiveRadarrBlock::Downloads => self.dispatch(RadarrEvent::GetDownloads.into()).await,
ActiveRadarrBlock::Movies => {
self.dispatch(RadarrEvent::GetMovies.into()).await;
self.dispatch(RadarrEvent::GetDownloads.into()).await;
},
_ => ()
}
ActiveRadarrBlock::MovieDetails => self.dispatch(RadarrEvent::GetMovieDetails.into()).await,
_ => (),
}
}
}