Almost kinda functional description box
This commit is contained in:
+23
-23
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user