Skip to content

Commit

Permalink
continue refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
nick42d committed Nov 28, 2024
1 parent 8603183 commit 31f282c
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 266 deletions.
2 changes: 2 additions & 0 deletions youtui/src/app/ui/action.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::app::component::actionhandler::Action;

#[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AppAction {
Expand Down
75 changes: 59 additions & 16 deletions youtui/src/app/ui/browser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,25 @@ use self::{
artistalbums::{albumsongs::AlbumSongsPanel, artistsearch::ArtistSearchPanel},
draw::draw_browser,
};
use super::AppCallback;
use super::{action::AppAction, AppCallback, WindowContext};
use crate::app::{
component::actionhandler::{
Action, Component, ComponentEffect, DominantKeyRouter, KeyRouter, Suggestable, TextHandler,
},
server::{
api::GetArtistSongsProgressUpdate, ArcServer, GetArtistSongs, SearchArtists, TaskMetadata,
},
structures::{ListStatus, SongListComponent},
view::{DrawableMut, Scrollable},
};
use crate::{
app::{component::actionhandler::DynKeybindsIter, keycommand::KeyCommand},
config::{Config, KeyEnum, KeyEnumKey},
config::Config,
core::send_or_error,
};
use crate::{
app::{
component::actionhandler::{
Component, ComponentEffect, DominantKeyRouter, KeyRouter, Suggestable, TextHandler,
},
server::{
api::GetArtistSongsProgressUpdate, ArcServer, GetArtistSongs, SearchArtists,
TaskMetadata,
},
structures::{ListStatus, SongListComponent},
view::{DrawableMut, Scrollable},
},
config::AppAction,
};
use async_callback_manager::{AsyncTask, Constraint};
use itertools::Either;
use serde::{Deserialize, Serialize};
use std::{iter::Iterator, mem};
use tokio::sync::mpsc;
use tracing::error;
Expand Down Expand Up @@ -52,6 +49,52 @@ pub struct Browser {
keybinds: Vec<KeyCommand<AppAction>>,
}

#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BrowserAction {
ViewPlaylist,
Search,
Left,
Right,
}

impl Action for BrowserAction {
type State = Browser;
fn context(&self) -> std::borrow::Cow<str> {
"Browser".into()
}
fn describe(&self) -> std::borrow::Cow<str> {
match self {
BrowserAction::ViewPlaylist => "View Playlist",
BrowserAction::Search => "Toggle Search",
BrowserAction::Left => "Left",
BrowserAction::Right => "Right",
}
.into()
}
async fn apply(
self,
state: &mut Self::State,
) -> crate::app::component::actionhandler::ComponentEffect<Self::State>
where
Self: Sized,
{
match self {
BrowserAction::Left => state.left(),
BrowserAction::Right => state.right(),
BrowserAction::ViewPlaylist => {
send_or_error(
&state.callback_tx,
AppCallback::ChangeContext(WindowContext::Playlist),
)
.await
}
BrowserAction::Search => state.handle_toggle_search(),
}
AsyncTask::new_no_op()
}
}

impl InputRouting {
pub fn left(&self) -> Self {
match self {
Expand Down
262 changes: 12 additions & 250 deletions youtui/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use crate::get_config_dir;
use crate::Result;
use async_callback_manager::AsyncTask;
use clap::ValueEnum;
use keybinds::YoutuiKeymap;
use keybinds::YoutuiModeNames;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::convert::Infallible;
Expand All @@ -20,6 +22,8 @@ use ytmapi_rs::auth::OAuthToken;

const CONFIG_FILE_NAME: &str = "config.toml";

pub mod keybinds;

#[derive(Serialize, Deserialize)]
pub enum ApiKey {
OAuthToken(OAuthToken),
Expand All @@ -37,6 +41,14 @@ impl std::fmt::Debug for ApiKey {
}
}

#[derive(ValueEnum, Copy, Clone, Default, Debug, Serialize, Deserialize)]
pub enum AuthType {
#[value(name = "oauth")]
OAuth,
#[default]
Browser,
}

#[derive(Debug)]
pub struct Config {
pub auth_type: AuthType,
Expand Down Expand Up @@ -68,256 +80,6 @@ impl TryFrom<ConfigIR> for Config {
}
}

#[derive(ValueEnum, Copy, Clone, Default, Debug, Serialize, Deserialize)]
pub enum AuthType {
#[value(name = "oauth")]
OAuth,
#[default]
Browser,
}

#[derive(Debug)]
pub struct YoutuiKeymap {
pub global: HashMap<Keybind, KeyEnum<AppAction>>,
pub playlist: HashMap<Keybind, KeyEnum<AppAction>>,
pub browser: HashMap<Keybind, KeyEnum<AppAction>>,
pub browser_artists: HashMap<Keybind, KeyEnum<AppAction>>,
pub browser_search: HashMap<Keybind, KeyEnum<AppAction>>,
pub browser_songs: HashMap<Keybind, KeyEnum<AppAction>>,
pub help: HashMap<Keybind, KeyEnum<AppAction>>,
pub sort: HashMap<Keybind, KeyEnum<AppAction>>,
pub filter: HashMap<Keybind, KeyEnum<AppAction>>,
pub text_entry: HashMap<Keybind, KeyEnum<AppAction>>,
pub list: HashMap<Keybind, KeyEnum<AppAction>>,
pub log: HashMap<Keybind, KeyEnum<AppAction>>,
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct YoutuiKeymapIR {
pub global: HashMap<Keybind, KeyEnumString>,
pub playlist: HashMap<Keybind, KeyEnumString>,
pub browser: HashMap<Keybind, KeyEnumString>,
pub browser_artists: HashMap<Keybind, KeyEnumString>,
pub browser_search: HashMap<Keybind, KeyEnumString>,
pub browser_songs: HashMap<Keybind, KeyEnumString>,
pub help: HashMap<Keybind, KeyEnumString>,
pub sort: HashMap<Keybind, KeyEnumString>,
pub filter: HashMap<Keybind, KeyEnumString>,
pub text_entry: HashMap<Keybind, KeyEnumString>,
pub list: HashMap<Keybind, KeyEnumString>,
pub log: HashMap<Keybind, KeyEnumString>,
}

impl TryFrom<YoutuiKeymapIR> for YoutuiKeymap {
type Error = String;
fn try_from(value: YoutuiKeymapIR) -> std::result::Result<Self, Self::Error> {
let YoutuiKeymapIR {
global,
playlist,
browser,
browser_artists,
browser_search,
browser_songs,
help,
sort,
filter,
text_entry,
list,
log,
} = value;
Ok(Self {
global: global
.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<std::result::Result<HashMap<_, _>, String>>()?,
playlist: playlist
.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<std::result::Result<HashMap<_, _>, String>>()?,
browser: browser
.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<std::result::Result<HashMap<_, _>, String>>()?,
browser_artists: browser_artists
.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<std::result::Result<HashMap<_, _>, String>>()?,
browser_search: browser_search
.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<std::result::Result<HashMap<_, _>, String>>()?,
browser_songs: browser_songs
.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<std::result::Result<HashMap<_, _>, String>>()?,
help: help
.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<std::result::Result<HashMap<_, _>, String>>()?,
sort: sort
.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<std::result::Result<HashMap<_, _>, String>>()?,
filter: filter
.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<std::result::Result<HashMap<_, _>, String>>()?,
text_entry: text_entry
.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<std::result::Result<HashMap<_, _>, String>>()?,
list: list
.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<std::result::Result<HashMap<_, _>, String>>()?,
log: log
.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<std::result::Result<HashMap<_, _>, String>>()?,
})
}
}

#[derive(Default, Debug, Serialize, Deserialize)]
#[serde(default)]
pub struct YoutuiModeNames {
global: HashMap<Keybind, ModeNameEnum>,
playlist: HashMap<Keybind, ModeNameEnum>,
browser: HashMap<Keybind, ModeNameEnum>,
browser_artists: HashMap<Keybind, ModeNameEnum>,
browser_search: HashMap<Keybind, ModeNameEnum>,
browser_songs: HashMap<Keybind, ModeNameEnum>,
help: HashMap<Keybind, ModeNameEnum>,
sort: HashMap<Keybind, ModeNameEnum>,
filter: HashMap<Keybind, ModeNameEnum>,
text_entry: HashMap<Keybind, ModeNameEnum>,
list: HashMap<Keybind, ModeNameEnum>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum KeyEnumString {
#[serde(deserialize_with = "super::core::string_or_struct")]
Key(KeyEnumKey<String>),
Mode(HashMap<Keybind, KeyEnumString>),
}

#[derive(Debug)]
pub enum KeyEnum<A: Default> {
Key(KeyEnumKey<A>),
Mode(HashMap<Keybind, KeyEnum<A>>),
}

impl TryFrom<KeyEnumString> for KeyEnum<AppAction> {
type Error = String;
fn try_from(value: KeyEnumString) -> std::result::Result<Self, Self::Error> {
let new: KeyEnum<AppAction> = match value {
KeyEnumString::Key(k) => KeyEnum::Key(k.try_map(TryInto::try_into)?),
KeyEnumString::Mode(m) => KeyEnum::Mode(
m.into_iter()
.map(|(k, a)| Ok::<_, String>((k, KeyEnum::<AppAction>::try_from(a)?)))
.collect::<std::result::Result<_, _>>()?,
),
};
Ok(new)
}
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct KeyEnumKey<A: Default> {
// Consider - can there be multiple actions?
// Consider - can an action access global commands? Or commands from another component?
// Consider - case where component has list and help keybinds, but some keybinds share a
// mode. What happens here.
pub action: A,
#[serde(default)]
pub value: usize,
#[serde(default)]
pub visibility: CommandVisibility,
}

impl<A: Default> KeyEnumKey<A> {
fn try_map<U: Default, E>(
self,
f: impl FnOnce(A) -> std::result::Result<U, E>,
) -> std::result::Result<KeyEnumKey<U>, E> {
let Self {
action,
value,
visibility,
} = self;
Ok(KeyEnumKey {
action: f(action)?,
value,
visibility,
})
}
}

impl FromStr for KeyEnumKey<String> {
type Err = Infallible;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
Ok(KeyEnumKey {
action: s.to_string(),
value: Default::default(),
visibility: Default::default(),
})
}
}

#[derive(PartialEq, Debug, Serialize, Deserialize)]
enum ModeNameEnum {
Submode(HashMap<Keybind, ModeNameEnum>),
#[serde(untagged)]
Name(String),
}

#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BrowserAction {
ViewPlaylist,
Search,
Left,
Right,
}

impl Action for BrowserAction {
type State = Browser;
fn context(&self) -> std::borrow::Cow<str> {
"Browser".into()
}
fn describe(&self) -> std::borrow::Cow<str> {
match self {
BrowserAction::ViewPlaylist => "View Playlist",
BrowserAction::Search => "Toggle Search",
BrowserAction::Left => "Left",
BrowserAction::Right => "Right",
}
.into()
}
async fn apply(
self,
state: &mut Self::State,
) -> crate::app::component::actionhandler::ComponentEffect<Self::State>
where
Self: Sized,
{
match self {
BrowserAction::Left => state.left(),
BrowserAction::Right => state.right(),
BrowserAction::ViewPlaylist => {
send_or_error(
&state.callback_tx,
AppCallback::ChangeContext(WindowContext::Playlist),
)
.await
}
BrowserAction::Search => state.handle_toggle_search(),
}
AsyncTask::new_no_op()
}
}
#[derive(PartialEq, Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum BrowserArtistsAction {
Expand Down

0 comments on commit 31f282c

Please sign in to comment.