Skip to content

Commit

Permalink
feat(launchpad): store discord username to disk
Browse files Browse the repository at this point in the history
  • Loading branch information
RolandSherwin authored and joshuef committed May 9, 2024
1 parent aac2f08 commit bbb5e6e
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 37 deletions.
1 change: 1 addition & 0 deletions node-launchpad/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub enum Action {
TabActions(TabActions),
SwitchScene(Scene),
SwitchInputMode(InputMode),
StoreDiscordUserName(String),

Tick,
Render,
Expand Down
18 changes: 14 additions & 4 deletions node-launchpad/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};
Expand All @@ -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<Box<dyn Component>>,
Expand All @@ -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![
Expand All @@ -51,7 +56,6 @@ impl App {
],
should_quit: false,
should_suspend: false,
config,
input_mode: InputMode::Navigation,
scene,
last_tick_key_events: Vec::new(),
Expand Down Expand Up @@ -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)?;
}
}
Expand Down Expand Up @@ -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() {
Expand Down
12 changes: 6 additions & 6 deletions node-launchpad/src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ pub trait Component {
/// # Returns
///
/// * `Result<Option<Action>>` - An action to be processed or none.
fn handle_events(&mut self, event: Option<Event>) -> Result<Option<Action>> {
fn handle_events(&mut self, event: Option<Event>) -> Result<Vec<Action>> {
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)
}
Expand All @@ -93,8 +93,8 @@ pub trait Component {
///
/// * `Result<Option<Action>>` - An action to be processed or none.
#[allow(unused_variables)]
fn handle_key_events(&mut self, key: KeyEvent) -> Result<Option<Action>> {
Ok(None)
fn handle_key_events(&mut self, key: KeyEvent) -> Result<Vec<Action>> {
Ok(vec![])
}
/// Handle mouse events and produce actions if necessary.
///
Expand All @@ -106,8 +106,8 @@ pub trait Component {
///
/// * `Result<Option<Action>>` - An action to be processed or none.
#[allow(unused_variables)]
fn handle_mouse_events(&mut self, mouse: MouseEvent) -> Result<Option<Action>> {
Ok(None)
fn handle_mouse_events(&mut self, mouse: MouseEvent) -> Result<Vec<Action>> {
Ok(vec![])
}
/// Update the state of the component based on a received action. (REQUIRED)
///
Expand Down
38 changes: 27 additions & 11 deletions node-launchpad/src/components/discord_username.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,43 +16,60 @@ 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,
// cache the old value incase user presses Esc.
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<Option<Action>> {
fn handle_key_events(&mut self, key: KeyEvent) -> Result<Vec<Action>> {
// 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)
Expand Down Expand Up @@ -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]);

Expand Down
6 changes: 3 additions & 3 deletions node-launchpad/src/components/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ pub struct Options {
}

impl Component for Options {
fn handle_key_events(&mut self, key: KeyEvent) -> Result<Option<Action>> {
fn handle_key_events(&mut self, key: KeyEvent) -> Result<Vec<Action>> {
// 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();
Expand All @@ -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<Option<Action>> {
Expand Down
50 changes: 38 additions & 12 deletions node-launchpad/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self> {
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)]
Expand Down Expand Up @@ -90,7 +116,7 @@ impl Config {
}
}

#[derive(Clone, Debug, Default, Deref, DerefMut)]
#[derive(Clone, Debug, Default, Deref, DerefMut, Serialize)]
pub struct KeyBindings(pub HashMap<Scene, HashMap<Vec<KeyEvent>, Action>>);

impl<'de> Deserialize<'de> for KeyBindings {
Expand Down Expand Up @@ -286,7 +312,7 @@ pub fn parse_key_sequence(raw: &str) -> Result<Vec<KeyEvent>, 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<Scene, HashMap<String, Style>>);

impl<'de> Deserialize<'de> for Styles {
Expand Down
2 changes: 1 addition & 1 deletion node-launchpad/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit bbb5e6e

Please sign in to comment.