From 20a6500632fb5ee9d0f720f83844ce45681d6c7a Mon Sep 17 00:00:00 2001 From: Xithrius Date: Mon, 24 Jul 2023 23:07:52 -0700 Subject: [PATCH] Enhanced keyboard bindings for debug/following --- src/handlers/app.rs | 36 +++++++------ src/twitch/oauth.rs | 6 +-- src/ui/components/chat.rs | 5 +- src/ui/components/debug.rs | 21 +++++++- src/ui/components/following.rs | 94 +++++++++++++++++++++++----------- 5 files changed, 107 insertions(+), 55 deletions(-) diff --git a/src/handlers/app.rs b/src/handlers/app.rs index 3e97d95..c705ccb 100644 --- a/src/handlers/app.rs +++ b/src/handlers/app.rs @@ -125,7 +125,7 @@ impl App { } if self.components.following.is_focused() { - let rect = centered_rect(60, 60, 10, size); + let rect = centered_rect(60, 60, 20, size); self.components.following.draw(f, rect, None); } else if self.components.debug.is_focused() { @@ -142,20 +142,26 @@ impl App { pub fn event(&mut self, event: &Event) -> Option { if let Event::Input(key) = event { - match key { - // Global keybinds - Key::Ctrl('d') => { - self.components.debug.toggle_focus(); - } - Key::Char('f') => { - self.components.following.toggle_focus(); - } - _ => { - return match self.state { - State::Dashboard => self.components.dashboard.event(event), - State::Normal => self.components.chat.event(event), - State::Help => self.components.help.event(event), - }; + if self.components.debug.is_focused() { + self.components.debug.event(event); + } else if self.components.following.is_focused() { + self.components.following.event(event); + } else { + match key { + // Global keybinds + Key::Ctrl('d') => { + self.components.debug.toggle_focus(); + } + Key::Char('f') => { + self.components.following.toggle_focus(); + } + _ => { + return match self.state { + State::Dashboard => self.components.dashboard.event(event), + State::Normal => self.components.chat.event(event), + State::Help => self.components.help.event(event), + }; + } } } } diff --git a/src/twitch/oauth.rs b/src/twitch/oauth.rs index c899262..2c7acf8 100644 --- a/src/twitch/oauth.rs +++ b/src/twitch/oauth.rs @@ -75,7 +75,7 @@ pub async fn get_channel_id(client: &Client, channel: &str) -> Result { .parse()?) } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug, Clone, Default)] #[allow(dead_code)] pub struct FollowingUser { broadcaster_id: String, @@ -84,13 +84,13 @@ pub struct FollowingUser { followed_at: String, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug, Clone, Default)] #[allow(dead_code)] struct Pagination { cursor: String, } -#[derive(Deserialize, Debug, Clone)] +#[derive(Deserialize, Debug, Clone, Default)] #[allow(dead_code)] pub struct FollowingList { total: u64, diff --git a/src/ui/components/chat.rs b/src/ui/components/chat.rs index 22e38c5..0eb908f 100644 --- a/src/ui/components/chat.rs +++ b/src/ui/components/chat.rs @@ -284,10 +284,7 @@ impl Component for ChatWidget { Block::default() .borders(Borders::ALL) .border_type(self.config.borrow().frontend.border_type.clone().into()) - .title(chat_title), // .style(match self.theme { - // Theme::Light => BORDER_NAME_LIGHT, - // _ => BORDER_NAME_DARK, - // }), + .title(chat_title), ) .style(Style::default().fg(Color::White)); diff --git a/src/ui/components/debug.rs b/src/ui/components/debug.rs index 20c07ff..07a0338 100644 --- a/src/ui/components/debug.rs +++ b/src/ui/components/debug.rs @@ -8,9 +8,9 @@ use tui::{ use crate::{ emotes::Emotes, - handlers::config::SharedCompleteConfig, + handlers::{config::SharedCompleteConfig, user_input::events::{Key, Event}}, ui::components::Component, - utils::text::{title_line, TitleStyle}, + utils::text::{title_line, TitleStyle}, terminal::TerminalAction, }; #[derive(Debug, Clone)] @@ -60,4 +60,21 @@ impl Component for DebugWidget { f.render_widget(Clear, area); f.render_widget(table, area); } + + fn event(&mut self, event: &Event) -> Option { + if let Event::Input(key) = event { + match key { + Key::Char('q') => return Some(TerminalAction::Quit), + Key::Esc => { + self.toggle_focus(); + + return Some(TerminalAction::BackOneLayer) + }, + Key::Ctrl('p') => panic!("Manual panic triggered by user."), + _ => {} + } + } + + None + } } diff --git a/src/ui/components/following.rs b/src/ui/components/following.rs index d65cccc..5bebc50 100644 --- a/src/ui/components/following.rs +++ b/src/ui/components/following.rs @@ -2,15 +2,19 @@ use tui::{ backend::Backend, layout::{Constraint, Rect}, style::{Color, Modifier, Style}, - widgets::{Block, Borders, Clear, Row, Table}, + widgets::{Block, Borders, Clear, Row, Table, TableState}, Frame, }; use crate::{ emotes::Emotes, - handlers::config::SharedCompleteConfig, + handlers::{ + config::SharedCompleteConfig, + user_input::events::{Event, Key}, + }, + terminal::TerminalAction, twitch::oauth::{get_channel_id, get_twitch_client, get_user_following, FollowingList}, - ui::components::Component, + ui::{components::Component, statics::NAME_MAX_CHARACTERS}, utils::text::{title_line, TitleStyle}, }; @@ -18,7 +22,8 @@ use crate::{ pub struct FollowingWidget { config: SharedCompleteConfig, focused: bool, - following: Option, + following: FollowingList, + state: TableState, } impl FollowingWidget { @@ -26,32 +31,42 @@ impl FollowingWidget { Self { config, focused: false, - following: None, + following: FollowingList::default(), + state: TableState::default(), } } - // pub fn get_following(&mut self) { - // let oauth_token = self.config.borrow().twitch.token.clone(); - // let app_user = self.config.borrow().twitch.username.clone(); + fn next(&mut self) { + let i = match self.state.selected() { + Some(i) => { + if i >= self.following.data.len() - 1 { + 0 + } else { + i + 1 + } + } + None => 0, + }; + self.state.select(Some(i)); + } - // let output = tokio::task::spawn_blocking(move || { - // let rt = tokio::runtime::Runtime::new().unwrap(); + fn previous(&mut self) { + let i = match self.state.selected() { + Some(i) => { + if i == 0 { + self.following.data.len() - 1 + } else { + i - 1 + } + } + None => 0, + }; + self.state.select(Some(i)); + } - // rt.block_on(async { - // let client = get_twitch_client(oauth_token).await.unwrap(); - - // let user_id = get_channel_id(&client, &app_user).await.unwrap(); - - // Some(get_user_following(&client, user_id).await) - // }) - // }); - - // mem::drop(output.and_then(|x| async move { - // self.following = x; - - // Ok(()) - // })); - // } + fn unselect(&mut self) { + self.state.select(None); + } pub async fn get_following(&mut self) { let oauth_token = self.config.borrow().twitch.token.clone(); @@ -61,7 +76,7 @@ impl FollowingWidget { let user_id = get_channel_id(&client, &app_user).await.unwrap(); - self.following = Some(get_user_following(&client, user_id).await); + self.following = get_user_following(&client, user_id).await; } pub const fn is_focused(&self) -> bool { @@ -77,14 +92,14 @@ impl Component for FollowingWidget { fn draw(&mut self, f: &mut Frame, area: Rect, _emotes: Option<&mut Emotes>) { let mut rows = vec![]; - if let Some(followed_channels) = self.following.clone() { - for channel in followed_channels.data { - rows.push(Row::new(vec![channel.broadcaster_name.clone()])); - } + for channel in self.following.clone().data { + rows.push(Row::new(vec![channel.broadcaster_name.clone()])); } let title_binding = [TitleStyle::Single("Following")]; + let constraint_binding = [Constraint::Length(NAME_MAX_CHARACTERS as u16)]; + let table = Table::new(rows) .block( Block::default() @@ -95,9 +110,26 @@ impl Component for FollowingWidget { .borders(Borders::ALL) .border_type(self.config.borrow().frontend.border_type.clone().into()), ) - .widths(&[Constraint::Length(10), Constraint::Length(10)]); + .widths(&constraint_binding); f.render_widget(Clear, area); f.render_widget(table, area); } + + fn event(&mut self, event: &Event) -> Option { + if let Event::Input(key) = event { + match key { + Key::Char('q') => return Some(TerminalAction::Quit), + Key::Esc => { + self.toggle_focus(); + + return Some(TerminalAction::BackOneLayer); + } + Key::Ctrl('p') => panic!("Manual panic triggered by user."), + _ => {} + } + } + + None + } }