Skip to content

Commit

Permalink
Removed stateupdater module - simplified architecture
Browse files Browse the repository at this point in the history
  • Loading branch information
nick42d committed Nov 30, 2023
1 parent b9d6f4d commit a81b091
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 158 deletions.
27 changes: 13 additions & 14 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
use self::statemanager::process_state_updates;
use self::taskmanager::TaskManager;
use super::appevent::{AppEvent, EventHandler};
use super::Result;
use crate::app::server::api::Api;
use crate::config::Config;
use crate::{get_data_dir, RuntimeInfo};
use crossterm::{
event::{DisableMouseCapture, EnableMouseCapture},
execute,
terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use ratatui::{backend::CrosstermBackend, Terminal};
use std::process;
use std::{io, sync::Arc};
use tracing::info;
use tracing_subscriber::prelude::*;
Expand All @@ -20,7 +16,6 @@ use ui::YoutuiWindow;
mod component;
mod musiccache;
mod server;
mod statemanager;
mod structures;
mod taskmanager;
mod ui;
Expand Down Expand Up @@ -82,22 +77,20 @@ impl Youtui {
loop {
match self.window_state.get_status() {
ui::AppStatus::Running => {
// Get the events from the event_handler and process them.
let msg = self.event_handler.next().await;
self.process_event(msg).await;
// Get the next event from the event_handler and process it.
self.handle_next_event().await;
// If any requests are in the queue, queue up the tasks on the server.
self.queue_server_tasks().await;
// Get the state update events from the task manager and process them.
let state_updates = self.task_manager.process_messages();
process_state_updates(&mut self.window_state, state_updates).await;
// Get the state update events from the task manager and apply them to the window state.
self.synchronize_state().await;
// Write to terminal, using UI state as the input
// We draw after handling the event, as the event could be a keypress we want to instantly react to.
self.terminal.draw(|f| {
ui::draw::draw_app(f, &mut self.window_state);
})?;
}
ui::AppStatus::Exiting(s) => {
// Once we're done running, destruct the terminal.
// Once we're done running, destruct the terminal and print the exit message.
destruct_terminal()?;
println!("{s}");
break;
Expand All @@ -106,8 +99,14 @@ impl Youtui {
}
Ok(())
}
async fn process_event(&mut self, msg: Option<AppEvent>) {
// TODO: Handle closed channel
async fn synchronize_state(&mut self) {
self.task_manager
.action_messages(&mut self.window_state)
.await;
}
async fn handle_next_event(&mut self) {
let msg = self.event_handler.next().await;
// TODO: Handle closed channel better
match msg {
Some(AppEvent::QuitSignal) => self
.window_state
Expand Down
83 changes: 0 additions & 83 deletions src/app/statemanager.rs

This file was deleted.

112 changes: 51 additions & 61 deletions src/app/taskmanager.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::server::{api, downloader, player};
use super::statemanager::StateUpdateMessage;
use super::structures::ListSongID;
use super::ui::YoutuiWindow;
use super::Youtui;
use crate::app::server::KillRequest;
use crate::app::server::{self, KillableTask};
use crate::config::ApiKey;
Expand Down Expand Up @@ -317,74 +318,65 @@ impl TaskManager {
self.tasks
.retain(|x| x.message.category() != request_category || x.id == id);
}
pub fn process_messages(&mut self) -> Vec<StateUpdateMessage> {
pub async fn action_messages(&mut self, ui_state: &mut YoutuiWindow) {
// XXX: Consider general case to check if task is valid.
// In this case, message could implement Task with get_id() function?
let mut state_update_list = Vec::new();
while let Ok(msg) = self.server_response_rx.try_recv() {
match msg {
server::Response::Api(msg) => {
if let Some(state_msg) = self.process_api_msg(msg) {
state_update_list.push(state_msg)
}
}
server::Response::Player(msg) => {
if let Some(state_msg) = self.process_player_msg(msg) {
state_update_list.push(state_msg)
}
}
server::Response::Api(msg) => self.process_api_msg(msg, ui_state).await,
server::Response::Player(msg) => self.process_player_msg(msg, ui_state).await,
server::Response::Downloader(msg) => {
if let Some(state_msg) = self.process_downloader_msg(msg) {
state_update_list.push(state_msg)
}
self.process_downloader_msg(msg, ui_state).await
}
}
};
}
state_update_list
}
pub fn process_api_msg(&self, msg: api::Response) -> Option<StateUpdateMessage> {
pub async fn process_api_msg(&self, msg: api::Response, ui_state: &mut YoutuiWindow) {
tracing::debug!("Processing {:?}", msg);
match msg {
api::Response::ReplaceArtistList(list, id) => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::ReplaceArtistList(list))
ui_state.handle_replace_artist_list(list).await;
}
api::Response::SearchArtistError(id) => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::HandleSearchArtistError)
ui_state.handle_search_artist_error();
}
api::Response::ReplaceSearchSuggestions(runs, id, search) => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::ReplaceSearchSuggestions(runs, search))
ui_state
.handle_replace_search_suggestions(runs, search)
.await;
}
api::Response::SongListLoading(id) => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::HandleSongListLoading)
ui_state.handle_song_list_loading();
}
api::Response::SongListLoaded(id) => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::HandleSongListLoaded)
ui_state.handle_song_list_loaded();
}
api::Response::NoSongsFound(id) => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::HandleNoSongsFound)
ui_state.handle_no_songs_found();
}
api::Response::SongsFound(id) => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::HandleSongsFound)
ui_state.handle_songs_found();
}
api::Response::AppendSongList {
song_list,
Expand All @@ -394,73 +386,71 @@ impl TaskManager {
id,
} => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::AppendSongList {
song_list,
album,
year,
artist,
})
ui_state.handle_append_song_list(song_list, album, year, artist);
}
api::Response::ApiError(e) => Some(StateUpdateMessage::HandleApiError(e)),
api::Response::ApiError(e) => ui_state.handle_api_error(e),
}
}
pub fn process_downloader_msg(&self, msg: downloader::Response) -> Option<StateUpdateMessage> {
pub async fn process_downloader_msg(
&self,
msg: downloader::Response,
ui_state: &mut YoutuiWindow,
) {
match msg {
downloader::Response::DownloadProgressUpdate(update_type, song_id, task_id) => {
if !self.is_task_valid(task_id) {
return None;
return;
}
Some(StateUpdateMessage::SetSongDownloadProgress(
update_type,
song_id,
))
ui_state
.handle_set_song_download_progress(update_type, song_id)
.await;
}
}
}
pub fn process_player_msg(&self, msg: player::Response) -> Option<StateUpdateMessage> {
pub async fn process_player_msg(&self, msg: player::Response, ui_state: &mut YoutuiWindow) {
match msg {
// XXX: Why are these not blockable tasks? As receiver responsible for race conditions?
// Is a task with race conditions a RaceConditionTask?
player::Response::DonePlaying(song_id) => {
Some(StateUpdateMessage::HandleDonePlaying(song_id))
ui_state.handle_done_playing(song_id).await;
}
player::Response::Paused(song_id, id) => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::SetToPaused(song_id))
ui_state.handle_set_to_paused(song_id).await;
}
player::Response::Playing(song_id, id) => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::SetToPlaying(song_id))
ui_state.handle_set_to_playing(song_id).await;
}
player::Response::Stopped(song_id, id) => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::SetToStopped(song_id))
ui_state.handle_set_to_stopped(song_id).await;
}
player::Response::StoppedAll(id) => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::SetAllToStopped)
ui_state.handle_set_all_to_stopped().await;
}
player::Response::ProgressUpdate(perc, song_id, id) => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::SetSongPlayProgress(perc, song_id))
ui_state.handle_set_song_play_progress(perc, song_id);
}
player::Response::VolumeUpdate(vol, id) => {
if !self.is_task_valid(id) {
return None;
return;
}
Some(StateUpdateMessage::SetVolume(vol))
ui_state.handle_set_volume(vol);
}
}
}
Expand Down

0 comments on commit a81b091

Please sign in to comment.