From bbb5e6e6e999521484e3391e4c3df2704278612c Mon Sep 17 00:00:00 2001 From: Roland Sherwin Date: Tue, 7 May 2024 02:23:45 +0530 Subject: [PATCH] feat(launchpad): store discord username to disk --- node-launchpad/src/action.rs | 1 + node-launchpad/src/app.rs | 18 +++++-- node-launchpad/src/components.rs | 12 ++--- .../src/components/discord_username.rs | 38 ++++++++++---- node-launchpad/src/components/options.rs | 6 +-- node-launchpad/src/config.rs | 50 ++++++++++++++----- node-launchpad/src/utils.rs | 2 +- 7 files changed, 90 insertions(+), 37 deletions(-) diff --git a/node-launchpad/src/action.rs b/node-launchpad/src/action.rs index 83b82824e3..dd7d113623 100644 --- a/node-launchpad/src/action.rs +++ b/node-launchpad/src/action.rs @@ -16,6 +16,7 @@ pub enum Action { TabActions(TabActions), SwitchScene(Scene), SwitchInputMode(InputMode), + StoreDiscordUserName(String), Tick, Render, diff --git a/node-launchpad/src/app.rs b/node-launchpad/src/app.rs index 9fc8867663..4d90edd2eb 100644 --- a/node-launchpad/src/app.rs +++ b/node-launchpad/src/app.rs @@ -11,7 +11,7 @@ use crate::{ components::{ discord_username::DiscordUsernameInputBox, footer::Footer, home::Home, tab::Tab, Component, }, - config::Config, + config::{AppData, Config}, mode::{InputMode, Scene}, tui, }; @@ -22,6 +22,7 @@ use tokio::sync::mpsc; pub struct App { pub config: Config, + pub app_data: AppData, pub tick_rate: f64, pub frame_rate: f64, pub components: Vec>, @@ -37,10 +38,14 @@ impl App { let tab = Tab::default(); let home = Home::new()?; let config = Config::new()?; - let discord_username_input = DiscordUsernameInputBox::default(); + let app_data = AppData::load()?; + let discord_username_input = + DiscordUsernameInputBox::new(app_data.discord_username.clone()); let footer = Footer::default(); let scene = tab.get_current_scene(); Ok(Self { + config, + app_data, tick_rate, frame_rate, components: vec![ @@ -51,7 +56,6 @@ impl App { ], should_quit: false, should_suspend: false, - config, input_mode: InputMode::Navigation, scene, last_tick_key_events: Vec::new(), @@ -106,7 +110,8 @@ impl App { }; } else if self.input_mode == InputMode::Entry { for component in self.components.iter_mut() { - if let Some(action) = component.handle_events(Some(e.clone()))? { + let send_back_actions = component.handle_events(Some(e.clone()))?; + for action in send_back_actions { action_tx.send(action)?; } } @@ -160,6 +165,11 @@ impl App { info!("Input mode switched to: {mode:?}"); self.input_mode = mode; } + Action::StoreDiscordUserName(ref username) => { + debug!("Storing discord username: {username:?}"); + self.app_data.discord_username.clone_from(username); + self.app_data.save()?; + } _ => {} } for component in self.components.iter_mut() { diff --git a/node-launchpad/src/components.rs b/node-launchpad/src/components.rs index 44770e9e5e..ee658c0841 100644 --- a/node-launchpad/src/components.rs +++ b/node-launchpad/src/components.rs @@ -75,11 +75,11 @@ pub trait Component { /// # Returns /// /// * `Result>` - An action to be processed or none. - fn handle_events(&mut self, event: Option) -> Result> { + fn handle_events(&mut self, event: Option) -> Result> { let r = match event { Some(Event::Key(key_event)) => self.handle_key_events(key_event)?, Some(Event::Mouse(mouse_event)) => self.handle_mouse_events(mouse_event)?, - _ => None, + _ => vec![], }; Ok(r) } @@ -93,8 +93,8 @@ pub trait Component { /// /// * `Result>` - An action to be processed or none. #[allow(unused_variables)] - fn handle_key_events(&mut self, key: KeyEvent) -> Result> { - Ok(None) + fn handle_key_events(&mut self, key: KeyEvent) -> Result> { + Ok(vec![]) } /// Handle mouse events and produce actions if necessary. /// @@ -106,8 +106,8 @@ pub trait Component { /// /// * `Result>` - An action to be processed or none. #[allow(unused_variables)] - fn handle_mouse_events(&mut self, mouse: MouseEvent) -> Result> { - Ok(None) + fn handle_mouse_events(&mut self, mouse: MouseEvent) -> Result> { + Ok(vec![]) } /// Update the state of the component based on a received action. (REQUIRED) /// diff --git a/node-launchpad/src/components/discord_username.rs b/node-launchpad/src/components/discord_username.rs index aebdf484e0..15193e6e55 100644 --- a/node-launchpad/src/components/discord_username.rs +++ b/node-launchpad/src/components/discord_username.rs @@ -16,7 +16,6 @@ use crossterm::event::{Event, KeyCode, KeyEvent}; use ratatui::{prelude::*, widgets::*}; use tui_input::{backend::crossterm::EventHandler, Input}; -#[derive(Default)] pub struct DiscordUsernameInputBox { show_scene: bool, discord_input_filed: Input, @@ -24,35 +23,53 @@ pub struct DiscordUsernameInputBox { old_value: String, } +impl DiscordUsernameInputBox { + pub fn new(username: String) -> Self { + Self { + show_scene: false, + discord_input_filed: Input::default().with_value(username), + old_value: Default::default(), + } + } +} + impl Component for DiscordUsernameInputBox { - fn handle_key_events(&mut self, key: KeyEvent) -> Result> { + fn handle_key_events(&mut self, key: KeyEvent) -> Result> { // while in entry mode, keybinds are not captured, so gotta exit entry mode from here let send_back = match key.code { KeyCode::Enter => { - // todo: save this value - Some(Action::SwitchScene(Scene::Home)) + let username = self.discord_input_filed.value().to_string(); + debug!("Got Enter, saving the discord username {username:?} and switching scene",); + vec![ + Action::StoreDiscordUserName(self.discord_input_filed.value().to_string()), + Action::SwitchScene(Scene::Home), + ] } KeyCode::Esc => { + debug!( + "Got Esc, restoring the old value {} and switching to home", + self.old_value + ); // reset to old value self.discord_input_filed = self .discord_input_filed .clone() .with_value(self.old_value.clone()); - Some(Action::SwitchScene(Scene::Home)) + vec![Action::SwitchScene(Scene::Home)] } - KeyCode::Char(' ') => None, + KeyCode::Char(' ') => vec![], KeyCode::Backspace => { // if max limit reached, we should allow Backspace to work. self.discord_input_filed.handle_event(&Event::Key(key)); - None + vec![] } _ => { // max 32 limit as per discord docs if self.discord_input_filed.value().len() >= 32 { - return Ok(None); + return Ok(vec![]); } self.discord_input_filed.handle_event(&Event::Key(key)); - None + vec![] } }; Ok(send_back) @@ -122,8 +139,7 @@ impl Component for DiscordUsernameInputBox { layer_one[1].x + ((self.discord_input_filed.visual_cursor()).max(scroll) - scroll) as u16 + 1, - // Move one line down, from the border to the input line - layer_one[1].y + 0, + layer_one[1].y, ); f.render_widget(input, layer_one[1]); diff --git a/node-launchpad/src/components/options.rs b/node-launchpad/src/components/options.rs index 6c3123c4ec..574fb0d8df 100644 --- a/node-launchpad/src/components/options.rs +++ b/node-launchpad/src/components/options.rs @@ -25,11 +25,11 @@ pub struct Options { } impl Component for Options { - fn handle_key_events(&mut self, key: KeyEvent) -> Result> { + fn handle_key_events(&mut self, key: KeyEvent) -> Result> { // while in entry mode, keybinds are not captured, so gotta exit entry mode from here match key.code { KeyCode::Esc => { - return Ok(Some(Action::SwitchInputMode(InputMode::Navigation))); + return Ok(vec![Action::SwitchInputMode(InputMode::Navigation)]); } KeyCode::Down => { // self.select_next_input_field(); @@ -40,7 +40,7 @@ impl Component for Options { _ => {} } self.input.handle_event(&Event::Key(key)); - Ok(None) + Ok(vec![]) } fn update(&mut self, action: Action) -> Result> { diff --git a/node-launchpad/src/config.rs b/node-launchpad/src/config.rs index cfa24bf6b2..402a583740 100644 --- a/node-launchpad/src/config.rs +++ b/node-launchpad/src/config.rs @@ -11,23 +11,49 @@ use color_eyre::eyre::Result; use crossterm::event::{KeyCode, KeyEvent, KeyModifiers}; use derive_deref::{Deref, DerefMut}; use ratatui::style::{Color, Modifier, Style}; -use serde::{de::Deserializer, Deserialize}; -use std::{collections::HashMap, path::PathBuf}; +use serde::{de::Deserializer, Deserialize, Serialize}; +use std::collections::HashMap; const CONFIG: &str = include_str!("../.config/config.json5"); -#[derive(Clone, Debug, Deserialize, Default)] -pub struct AppConfig { +#[derive(Clone, Debug, Deserialize, Default, Serialize)] +pub struct AppData { #[serde(default)] - pub _data_dir: PathBuf, - #[serde(default)] - pub _config_dir: PathBuf, + pub discord_username: String, +} + +impl AppData { + pub fn load() -> Result { + let config_dir = crate::utils::get_config_dir() + .map_err(|_| color_eyre::eyre::eyre!("Could not obtain config dir"))?; + let config_path = config_dir.join("app_data.json"); + + if !config_path.exists() { + return Ok(Self::default()); + } + + let data = std::fs::read_to_string(config_path) + .map_err(|_| color_eyre::eyre::eyre!("Failed to read app data file"))?; + let app_data: AppData = serde_json::from_str(&data) + .map_err(|_| color_eyre::eyre::eyre!("Failed to parse app data"))?; + + Ok(app_data) + } + + pub fn save(&self) -> Result<()> { + let config_dir = crate::utils::get_config_dir() + .map_err(|_| config::ConfigError::Message("Could not obtain data dir".to_string()))?; + + let config_path = config_dir.join("app_data.json"); + let serialized_config = serde_json::to_string_pretty(&self)?; + std::fs::write(config_path, serialized_config)?; + + Ok(()) + } } -#[derive(Clone, Debug, Default, Deserialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Config { - #[serde(default, flatten)] - pub config: AppConfig, #[serde(default)] pub keybindings: KeyBindings, #[serde(default)] @@ -90,7 +116,7 @@ impl Config { } } -#[derive(Clone, Debug, Default, Deref, DerefMut)] +#[derive(Clone, Debug, Default, Deref, DerefMut, Serialize)] pub struct KeyBindings(pub HashMap, Action>>); impl<'de> Deserialize<'de> for KeyBindings { @@ -286,7 +312,7 @@ pub fn parse_key_sequence(raw: &str) -> Result, String> { sequences.into_iter().map(parse_key_event).collect() } -#[derive(Clone, Debug, Default, Deref, DerefMut)] +#[derive(Clone, Debug, Default, Deref, DerefMut, Serialize)] pub struct Styles(pub HashMap>); impl<'de> Deserialize<'de> for Styles { diff --git a/node-launchpad/src/utils.rs b/node-launchpad/src/utils.rs index fe82f6d95d..e9b70722b6 100644 --- a/node-launchpad/src/utils.rs +++ b/node-launchpad/src/utils.rs @@ -99,7 +99,7 @@ pub fn initialize_logging() -> Result<()> { std::env::set_var( "RUST_LOG", std::env::var("RUST_LOG") - .unwrap_or_else(|_| format!("{}=info,debug", env!("CARGO_CRATE_NAME"))), + .unwrap_or_else(|_| format!("{}=trace,debug", env!("CARGO_CRATE_NAME"))), ); let file_subscriber = tracing_subscriber::fmt::layer() .with_file(true)