From d4614471e103e6b749b52c4a0d204da359869faf Mon Sep 17 00:00:00 2001 From: Nick Dowsett Date: Thu, 30 Nov 2023 22:34:38 +0800 Subject: [PATCH] Moved callbacks to top level of app, reduced need to pass around multiple senders --- src/app.rs | 141 +++++++++++++++++++++++-- src/app/structures.rs | 4 +- src/app/taskmanager.rs | 9 +- src/app/ui.rs | 230 ++++++++++------------------------------- src/app/ui/browser.rs | 34 +++--- src/app/ui/logger.rs | 8 +- src/app/ui/playlist.rs | 26 ++--- 7 files changed, 232 insertions(+), 220 deletions(-) diff --git a/src/app.rs b/src/app.rs index a9d362b7..602e72d7 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,6 +1,10 @@ -use self::taskmanager::TaskManager; +use self::structures::{ListSong, ListSongID}; +use self::taskmanager::{AppRequest, TaskManager}; +use self::ui::WindowContext; use super::appevent::{AppEvent, EventHandler}; use super::Result; +use crate::core::send_or_error; +use crate::error::Error; use crate::{get_data_dir, RuntimeInfo}; use crossterm::{ event::{DisableMouseCapture, EnableMouseCapture}, @@ -8,10 +12,13 @@ use crossterm::{ terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; use ratatui::{backend::CrosstermBackend, Terminal}; +use std::borrow::Cow; use std::{io, sync::Arc}; +use tokio::sync::mpsc; use tracing::info; use tracing_subscriber::prelude::*; use ui::YoutuiWindow; +use ytmapi_rs::{ChannelID, VideoID}; mod component; mod musiccache; @@ -21,14 +28,48 @@ mod taskmanager; mod ui; mod view; +const CALLBACK_CHANNEL_SIZE: usize = 64; const EVENT_CHANNEL_SIZE: usize = 256; const LOG_FILE_NAME: &str = "debug.log"; pub struct Youtui { + status: AppStatus, event_handler: EventHandler, window_state: YoutuiWindow, - terminal: Terminal>, task_manager: TaskManager, + callback_rx: mpsc::Receiver, + terminal: Terminal>, +} + +#[derive(PartialEq)] +pub enum AppStatus { + Running, + // Cow: Message + Exiting(Cow<'static, str>), +} + +// A callback from one of the application components to the top level. +#[derive(Debug)] +pub enum AppCallback { + DownloadSong(VideoID<'static>, ListSongID), + GetVolume, + GetProgress(ListSongID), + Quit, + ChangeContext(WindowContext), + Next, + Prev, + // Perhaps shiould not be here. + HandleApiError(Error), + IncreaseVolume(i8), + SearchArtist(String), + GetSearchSuggestions(String), + GetArtistSongs(ChannelID<'static>), + AddSongsToPlaylist(Vec), + AddSongsToPlaylistAndPlay(Vec), + PlaySong(Arc>, ListSongID), + PausePlay(ListSongID), + Stop(ListSongID), + StopAll, } impl Youtui { @@ -61,24 +102,30 @@ impl Youtui { let _ = destruct_terminal(); println!("{}", panic_info); })); + // Setup components + let (callback_tx, callback_rx) = mpsc::channel(CALLBACK_CHANNEL_SIZE); let task_manager = taskmanager::TaskManager::new(api_key); let backend = CrosstermBackend::new(stdout); let terminal = Terminal::new(backend)?; let event_handler = EventHandler::new(EVENT_CHANNEL_SIZE)?; - let window_state = YoutuiWindow::new(task_manager.get_sender_clone().clone()); + let window_state = YoutuiWindow::new(callback_tx); Ok(Youtui { + status: AppStatus::Running, terminal, event_handler, window_state, task_manager, + callback_rx, }) } pub async fn run(&mut self) -> Result<()> { loop { - match self.window_state.get_status() { - ui::AppStatus::Running => { + match &self.status { + AppStatus::Running => { // Get the next event from the event_handler and process it. self.handle_next_event().await; + // Process any callbacks in the queue. + self.process_callbacks().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 apply them to the window state. @@ -89,7 +136,7 @@ impl Youtui { ui::draw::draw_app(f, &mut self.window_state); })?; } - ui::AppStatus::Exiting(s) => { + AppStatus::Exiting(s) => { // Once we're done running, destruct the terminal and print the exit message. destruct_terminal()?; println!("{s}"); @@ -108,9 +155,9 @@ impl Youtui { let msg = self.event_handler.next().await; // TODO: Handle closed channel better match msg { - Some(AppEvent::QuitSignal) => self - .window_state - .set_status(ui::AppStatus::Exiting("Quit signal received".into())), + Some(AppEvent::QuitSignal) => { + self.status = AppStatus::Exiting("Quit signal received".into()) + } Some(AppEvent::Crossterm(e)) => self.window_state.handle_event(e).await, // XXX: Should be try_poll or similar? Poll the Future but don't await it? Some(AppEvent::Tick) => self.window_state.handle_tick().await, @@ -120,6 +167,82 @@ impl Youtui { async fn queue_server_tasks(&mut self) { self.task_manager.process_requests().await; } + pub async fn process_callbacks(&mut self) { + while let Ok(msg) = self.callback_rx.try_recv() { + match msg { + AppCallback::DownloadSong(video_id, playlist_id) => { + self.task_manager + .send_request(AppRequest::Download(video_id, playlist_id)) + .await; + } + AppCallback::Quit => self.status = AppStatus::Exiting("Quitting".into()), + AppCallback::HandleApiError(e) => { + self.status = AppStatus::Exiting(format!("{e}").into()) + } + + AppCallback::ChangeContext(context) => { + self.window_state.handle_change_context(context) + } + AppCallback::Next => self.window_state.handle_next().await, + AppCallback::Prev => self.window_state.handle_previous().await, + AppCallback::IncreaseVolume(i) => { + // Update state first for immediate visual feedback + self.window_state.increase_volume(i).await; + self.task_manager + .send_request(AppRequest::IncreaseVolume(i)) + .await; + } + AppCallback::GetSearchSuggestions(text) => { + self.task_manager + .send_request(AppRequest::GetSearchSuggestions(text)) + .await; + } + AppCallback::SearchArtist(artist) => { + self.task_manager + .send_request(AppRequest::SearchArtists(artist)) + .await; + } + AppCallback::GetArtistSongs(id) => { + self.task_manager + .send_request(AppRequest::GetArtistSongs(id)) + .await; + } + AppCallback::AddSongsToPlaylist(song_list) => { + self.window_state.handle_add_songs_to_playlist(song_list); + } + AppCallback::AddSongsToPlaylistAndPlay(song_list) => { + self.window_state + .handle_add_songs_to_playlist_and_play(song_list) + .await + } + AppCallback::PlaySong(song, id) => { + self.task_manager + .send_request(AppRequest::PlaySong(song, id)) + .await; + } + + AppCallback::PausePlay(id) => { + self.task_manager + .send_request(AppRequest::PausePlay(id)) + .await; + } + AppCallback::Stop(id) => { + self.task_manager.send_request(AppRequest::Stop(id)).await; + } + AppCallback::StopAll => { + self.task_manager.send_request(AppRequest::StopAll).await; + } + AppCallback::GetVolume => { + self.task_manager.send_request(AppRequest::GetVolume).await; + } + AppCallback::GetProgress(id) => { + self.task_manager + .send_request(AppRequest::GetPlayProgress(id)) + .await; + } + } + } + } } /// Cleanly exit the tui diff --git a/src/app/structures.rs b/src/app/structures.rs index b99555b6..61b3d437 100644 --- a/src/app/structures.rs +++ b/src/app/structures.rs @@ -24,7 +24,7 @@ pub struct ListSongID(usize); #[derive(Clone, PartialEq, Copy, Debug, Default, PartialOrd)] pub struct Percentage(pub u8); -#[derive(Clone)] +#[derive(Clone, Debug)] pub struct ListSong { pub raw: SongResult, pub download_status: DownloadStatus, @@ -42,7 +42,7 @@ pub enum ListStatus { Error, } -#[derive(Clone)] +#[derive(Clone, Debug)] pub enum DownloadStatus { None, Queued, diff --git a/src/app/taskmanager.rs b/src/app/taskmanager.rs index 580f793c..e1725aeb 100644 --- a/src/app/taskmanager.rs +++ b/src/app/taskmanager.rs @@ -108,15 +108,12 @@ impl TaskManager { request_rx, } } - pub fn get_sender_clone(&self) -> mpsc::Sender { - self.request_tx.clone() - } pub async fn process_requests(&mut self) { while let Ok(msg) = self.request_rx.try_recv() { self.send_request(msg).await; } } - async fn send_request(&mut self, request: AppRequest) -> Result<()> { + pub async fn send_request(&mut self, request: AppRequest) { let (kill_tx, kill_rx) = tokio::sync::oneshot::channel(); // NOTE: We allocate as we want to keep a copy of the same message that was sent. let id = self.add_task(kill_tx, request.clone()); @@ -137,7 +134,6 @@ impl TaskManager { AppRequest::StopAll => self.spawn_stop_all(id).await, AppRequest::PausePlay(song_id) => self.spawn_pause_play(song_id, id).await, }; - Ok(()) } // TODO: Consider if this should create it's own channel and return a KillableTask. fn add_task( @@ -390,7 +386,8 @@ impl TaskManager { } ui_state.handle_append_song_list(song_list, album, year, artist); } - api::Response::ApiError(e) => ui_state.handle_api_error(e), + // XXX: Improve routing for this action. + api::Response::ApiError(e) => ui_state.handle_api_error(e).await, } } pub async fn process_downloader_msg( diff --git a/src/app/ui.rs b/src/app/ui.rs index ed2f65d3..a87a7387 100644 --- a/src/app/ui.rs +++ b/src/app/ui.rs @@ -12,73 +12,33 @@ use super::component::actionhandler::{ Action, ActionHandler, ActionProcessor, DisplayableKeyRouter, KeyHandleOutcome, KeyHandler, KeyRouter, Keybind, KeybindVisibility, Keymap, TextHandler, }; -use super::server; use super::structures::*; -use super::taskmanager::{AppRequest, TaskID}; +use super::AppCallback; use crate::app::server::downloader::DownloadProgressUpdateType; use crate::core::send_or_error; use crate::error::Error; use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers}; use ratatui::widgets::{ListState, TableState}; use std::borrow::Cow; -use std::sync::Arc; use tokio::sync::mpsc; -use tracing::error; -use ytmapi_rs::common::{SearchSuggestion, TextRun}; -use ytmapi_rs::{ - parse::{SearchResultArtist, SongResult}, - ChannelID, VideoID, -}; +use ytmapi_rs::common::SearchSuggestion; +use ytmapi_rs::parse::{SearchResultArtist, SongResult}; const PAGE_KEY_SCROLL_AMOUNT: isize = 10; -const CHANNEL_SIZE: usize = 256; const VOL_TICK: i8 = 5; -#[deprecated] -pub struct BasicCommand { - key: KeyCode, - name: String, -} -#[derive(PartialEq)] -pub enum AppStatus { - Running, - // Cow: Message - Exiting(Cow<'static, str>), -} - // Which app level keyboard shortcuts function. // What is displayed in header // The main pane of the application // XXX: This is a bit like a route. +#[derive(Debug)] pub enum WindowContext { Browser, Playlist, Logs, } -// A callback from one of the application components to the top level. -// TODO: Shift these up to App. Then our UI want need to hold as many channels. -pub enum UIMessage { - DownloadSong(VideoID<'static>, ListSongID), - GetVolume, - GetProgress(ListSongID), - Quit, - ChangeContext(WindowContext), - Next, - Prev, - IncreaseVolume(i8), - SearchArtist(String), - GetSearchSuggestions(String), - GetArtistSongs(ChannelID<'static>), - AddSongsToPlaylist(Vec), - AddSongsToPlaylistAndPlay(Vec), - PlaySong(Arc>, ListSongID), - PausePlay(ListSongID), - Stop(ListSongID), - StopAll, -} - -// An action that can be triggered from a keybind. +// An Action that can be triggered from a keybind. #[derive(Clone, Debug, PartialEq)] pub enum UIAction { Quit, @@ -94,15 +54,12 @@ pub enum UIAction { } pub struct YoutuiWindow { - status: AppStatus, context: WindowContext, prev_context: WindowContext, playlist: Playlist, browser: Browser, logger: Logger, - _ui_tx: mpsc::Sender, - ui_rx: mpsc::Receiver, - task_manager_request_tx: mpsc::Sender, + callback_tx: mpsc::Sender, keybinds: Vec>, key_stack: Vec, help_shown: bool, @@ -195,9 +152,9 @@ impl ActionHandler for YoutuiWindow { UIAction::StepVolDown => self.handle_increase_volume(-VOL_TICK).await, UIAction::Browser(b) => self.browser.handle_action(b).await, UIAction::Playlist(b) => self.playlist.handle_action(b).await, - UIAction::Quit => self.quit(), + UIAction::Quit => send_or_error(&self.callback_tx, AppCallback::Quit).await, UIAction::ToggleHelp => self.help_shown = !self.help_shown, - UIAction::ViewLogs => self.change_context(WindowContext::Logs), + UIAction::ViewLogs => self.handle_change_context(WindowContext::Logs), } } } @@ -271,123 +228,51 @@ impl TextHandler for YoutuiWindow { } impl YoutuiWindow { - pub fn new(task_manager_request_tx: mpsc::Sender) -> YoutuiWindow { + pub fn new(callback_tx: mpsc::Sender) -> YoutuiWindow { // TODO: derive default - let (ui_tx, ui_rx) = mpsc::channel(CHANNEL_SIZE); YoutuiWindow { - status: AppStatus::Running, context: WindowContext::Browser, prev_context: WindowContext::Browser, - playlist: Playlist::new(ui_tx.clone()), - browser: Browser::new(ui_tx.clone()), - logger: Logger::new(ui_tx.clone()), - _ui_tx: ui_tx, - ui_rx, + playlist: Playlist::new(callback_tx.clone()), + browser: Browser::new(callback_tx.clone()), + logger: Logger::new(callback_tx.clone()), keybinds: global_keybinds(), key_stack: Vec::new(), help_shown: false, - task_manager_request_tx, mutable_state: Default::default(), + callback_tx, } } - pub fn get_status(&self) -> &AppStatus { - &self.status - } - pub fn set_status(&mut self, new_status: AppStatus) { - self.status = new_status; + // Splitting out event types removes one layer of indentation. + pub async fn handle_event(&mut self, event: crossterm::event::Event) { + match event { + Event::Key(k) => self.handle_key_event(k).await, + Event::Mouse(m) => self.handle_mouse_event(m), + other => tracing::warn!("Received unimplemented {:?} event", other), + } } pub async fn handle_tick(&mut self) { self.playlist.handle_tick().await; - self.process_ui_messages().await; - } - pub fn quit(&mut self) { - self.status = super::ui::AppStatus::Exiting("Quitting".into()); - } - pub async fn process_ui_messages(&mut self) { - while let Ok(msg) = self.ui_rx.try_recv() { - match msg { - UIMessage::DownloadSong(video_id, playlist_id) => { - send_or_error( - &self.task_manager_request_tx, - AppRequest::Download(video_id, playlist_id), - ) - .await; - } - UIMessage::Quit => self.quit(), - - UIMessage::ChangeContext(context) => self.change_context(context), - UIMessage::Next => self.playlist.handle_next().await, - UIMessage::Prev => self.playlist.handle_previous().await, - UIMessage::IncreaseVolume(i) => { - self.handle_increase_volume(i).await; - } - UIMessage::GetSearchSuggestions(text) => { - send_or_error( - &self.task_manager_request_tx, - AppRequest::GetSearchSuggestions(text), - ) - .await; - } - UIMessage::SearchArtist(artist) => { - send_or_error( - &self.task_manager_request_tx, - AppRequest::SearchArtists(artist), - ) - .await; - } - UIMessage::GetArtistSongs(id) => { - send_or_error( - &self.task_manager_request_tx, - AppRequest::GetArtistSongs(id), - ) - .await; - } - UIMessage::AddSongsToPlaylist(song_list) => { - self.playlist.push_song_list(song_list); - } - UIMessage::AddSongsToPlaylistAndPlay(song_list) => { - self.playlist.reset().await; - let id = self.playlist.push_song_list(song_list); - self.playlist.play_song_id(id).await; - } - UIMessage::PlaySong(song, id) => { - send_or_error( - &self.task_manager_request_tx, - AppRequest::PlaySong(song, id), - ) - .await; - } - - UIMessage::PausePlay(id) => { - send_or_error(&self.task_manager_request_tx, AppRequest::PausePlay(id)).await; - } - UIMessage::Stop(id) => { - send_or_error(&self.task_manager_request_tx, AppRequest::Stop(id)).await; - } - UIMessage::StopAll => { - send_or_error(&self.task_manager_request_tx, AppRequest::StopAll).await; - } - UIMessage::GetVolume => { - send_or_error(&self.task_manager_request_tx, AppRequest::GetVolume).await; - } - UIMessage::GetProgress(id) => { - send_or_error( - &self.task_manager_request_tx, - AppRequest::GetPlayProgress(id), - ) - .await; - } - } + } + async fn handle_key_event(&mut self, key_event: crossterm::event::KeyEvent) { + if self.handle_text_entry(key_event) { + return; } + self.key_stack.push(key_event); + self.global_handle_key_stack().await; } - async fn handle_increase_volume(&mut self, inc: i8) { + fn handle_mouse_event(&mut self, mouse_event: crossterm::event::MouseEvent) { + tracing::warn!("Received unimplemented {:?} mouse event", mouse_event); + } + // XXX: Should not be here, but required for now due to callback routing. + pub async fn handle_api_error(&mut self, e: Error) { + send_or_error(&self.callback_tx, AppCallback::HandleApiError(e)).await; + } + + pub async fn handle_increase_volume(&mut self, inc: i8) { // Visually update the state first for instant feedback. - self.playlist.increase_volume(inc); - send_or_error( - &self.task_manager_request_tx, - AppRequest::IncreaseVolume(inc), - ) - .await; + self.increase_volume(inc); + send_or_error(&self.callback_tx, AppCallback::IncreaseVolume(inc)).await; } pub async fn handle_done_playing(&mut self, id: ListSongID) { self.playlist.handle_done_playing(id).await @@ -407,8 +292,11 @@ impl YoutuiWindow { pub fn handle_set_volume(&mut self, p: Percentage) { self.playlist.handle_set_volume(p) } - pub fn handle_api_error(&mut self, e: Error) { - self.set_status(AppStatus::Exiting(e.to_string().into())); + pub async fn handle_next(&mut self) { + self.playlist.handle_next().await; + } + pub async fn handle_previous(&mut self) { + self.playlist.handle_previous().await; } pub fn handle_set_song_play_progress(&mut self, f: f64, id: ListSongID) { self.playlist.handle_set_song_play_progress(f, id); @@ -451,30 +339,21 @@ impl YoutuiWindow { self.browser .handle_append_song_list(song_list, album, year, artist) } + pub fn handle_add_songs_to_playlist(&mut self, song_list: Vec) { + let _ = self.playlist.push_song_list(song_list); + } + pub async fn handle_add_songs_to_playlist_and_play(&mut self, song_list: Vec) { + self.playlist.reset().await; + let id = self.playlist.push_song_list(song_list); + self.playlist.play_song_id(id).await; + } pub fn handle_songs_found(&mut self) { self.browser.handle_songs_found(); } pub fn handle_search_artist_error(&mut self) { self.browser.handle_search_artist_error(); } - // Splitting out event types removes one layer of indentation. - pub async fn handle_event(&mut self, event: crossterm::event::Event) { - match event { - Event::Key(k) => self.handle_key_event(k).await, - Event::Mouse(m) => self.handle_mouse_event(m), - other => tracing::warn!("Received unimplemented {:?} event", other), - } - } - async fn handle_key_event(&mut self, key_event: crossterm::event::KeyEvent) { - if self.handle_text_entry(key_event) { - return; - } - self.key_stack.push(key_event); - self.global_handle_key_stack().await; - } - fn handle_mouse_event(&mut self, mouse_event: crossterm::event::MouseEvent) { - tracing::warn!("Received unimplemented {:?} mouse event", mouse_event); - } + async fn global_handle_key_stack(&mut self) { // First handle my own keybinds, otherwise forward. if let KeyHandleOutcome::ActionHandled = @@ -495,7 +374,12 @@ impl YoutuiWindow { fn key_pending(&self) -> bool { !self.key_stack.is_empty() } - fn change_context(&mut self, new_context: WindowContext) { + + /// Visually increment the volume, note, does not actually change the volume. + pub async fn increase_volume(&mut self, inc: i8) { + self.playlist.increase_volume(inc); + } + pub fn handle_change_context(&mut self, new_context: WindowContext) { std::mem::swap(&mut self.context, &mut self.prev_context); self.context = new_context; } diff --git a/src/app/ui/browser.rs b/src/app/ui/browser.rs index 847b840b..4717f174 100644 --- a/src/app/ui/browser.rs +++ b/src/app/ui/browser.rs @@ -2,7 +2,7 @@ use self::{ artistalbums::{AlbumSongsPanel, ArtistAction, ArtistSearchPanel, ArtistSongsAction}, draw::draw_browser, }; -use super::{UIMessage, WindowContext, YoutuiMutableState}; +use super::{AppCallback, WindowContext, YoutuiMutableState}; use crate::app::{ component::actionhandler::{ Action, ActionHandler, ActionProcessor, KeyHandler, KeyRouter, Suggestable, TextHandler, @@ -40,7 +40,7 @@ pub enum InputRouting { } pub struct Browser { - ui_tx: mpsc::Sender, + ui_tx: mpsc::Sender, pub input_routing: InputRouting, pub prev_input_routing: InputRouting, pub artist_list: ArtistSearchPanel, @@ -212,7 +212,7 @@ impl ActionHandler for Browser { BrowserAction::ViewPlaylist => { send_or_error( &self.ui_tx, - UIMessage::ChangeContext(WindowContext::Playlist), + AppCallback::ChangeContext(WindowContext::Playlist), ) .await } @@ -223,7 +223,7 @@ impl ActionHandler for Browser { // KeyCode::F(3) => self.artist_list.push_sort_command("test".to_owned()), } impl Browser { - pub fn new(ui_tx: mpsc::Sender) -> Self { + pub fn new(ui_tx: mpsc::Sender) -> Self { Self { ui_tx, artist_list: ArtistSearchPanel::new(), @@ -259,7 +259,7 @@ impl Browser { self.artist_list.search.search_suggestions.clear(); return; } - if let Err(e) = self.ui_tx.try_send(UIMessage::GetSearchSuggestions( + if let Err(e) = self.ui_tx.try_send(AppCallback::GetSearchSuggestions( self.artist_list.search.search_contents.clone(), )) { error!("Error <{e}> recieved sending message") @@ -273,7 +273,7 @@ impl Browser { if let Some(cur_song) = self.album_songs_list.list.list.get(cur_song_idx) { send_or_error( &self.ui_tx, - UIMessage::AddSongsToPlaylistAndPlay(vec![cur_song.clone()]), + AppCallback::AddSongsToPlaylistAndPlay(vec![cur_song.clone()]), ) .await; } @@ -292,7 +292,11 @@ impl Browser { .skip(cur_song) .cloned() .collect(); - send_or_error(&self.ui_tx, UIMessage::AddSongsToPlaylistAndPlay(song_list)).await; + send_or_error( + &self.ui_tx, + AppCallback::AddSongsToPlaylistAndPlay(song_list), + ) + .await; // XXX: Do we want to indicate that song has been added to playlist? } async fn add_songs_to_playlist(&mut self) { @@ -308,7 +312,7 @@ impl Browser { .skip(cur_song) .cloned() .collect(); - send_or_error(&self.ui_tx, UIMessage::AddSongsToPlaylist(song_list)).await; + send_or_error(&self.ui_tx, AppCallback::AddSongsToPlaylist(song_list)).await; // XXX: Do we want to indicate that song has been added to playlist? } async fn add_song_to_playlist(&mut self) { @@ -319,7 +323,7 @@ impl Browser { if let Some(cur_song) = self.album_songs_list.list.list.get(cur_song_idx) { send_or_error( &self.ui_tx, - UIMessage::AddSongsToPlaylist(vec![cur_song.clone()]), + AppCallback::AddSongsToPlaylist(vec![cur_song.clone()]), ) .await; } @@ -341,7 +345,7 @@ impl Browser { .filter(|song| song.get_album() == cur_song.get_album()) .cloned() .collect(); - send_or_error(&self.ui_tx, UIMessage::AddSongsToPlaylist(song_list)).await; + send_or_error(&self.ui_tx, AppCallback::AddSongsToPlaylist(song_list)).await; // XXX: Do we want to indicate that song has been added to playlist? } async fn play_album(&mut self) { @@ -361,7 +365,11 @@ impl Browser { // XXX: Could instead be inside an Rc. .cloned() .collect(); - send_or_error(&self.ui_tx, UIMessage::AddSongsToPlaylistAndPlay(song_list)).await; + send_or_error( + &self.ui_tx, + AppCallback::AddSongsToPlaylistAndPlay(song_list), + ) + .await; // XXX: Do we want to indicate that song has been added to playlist? } async fn get_songs(&mut self) { @@ -379,13 +387,13 @@ impl Browser { error!("Tried to get item from list with index out of range"); return; }; - send_or_error(&self.ui_tx, UIMessage::GetArtistSongs(cur_artist_id)).await; + send_or_error(&self.ui_tx, AppCallback::GetArtistSongs(cur_artist_id)).await; tracing::info!("Sent request to UI to get songs"); } async fn search(&mut self) { self.artist_list.close_search(); let search_query = self.artist_list.search.take_text(); - send_or_error(&self.ui_tx, UIMessage::SearchArtist(search_query)).await; + send_or_error(&self.ui_tx, AppCallback::SearchArtist(search_query)).await; tracing::info!("Sent request to UI to search"); } pub fn handle_search_artist_error(&mut self) { diff --git a/src/app/ui/logger.rs b/src/app/ui/logger.rs index 290cb6d5..8ad67ca6 100644 --- a/src/app/ui/logger.rs +++ b/src/app/ui/logger.rs @@ -15,7 +15,7 @@ use crate::app::{ component::actionhandler::{ Action, ActionHandler, ActionProcessor, KeyHandler, KeyRouter, Keybind, TextHandler, }, - ui::UIMessage, + ui::AppCallback, view::Drawable, }; @@ -48,7 +48,7 @@ impl Action for LoggerAction { } pub struct Logger { logger_state: tui_logger::TuiWidgetState, - ui_tx: Sender, + ui_tx: Sender, keybinds: Vec>, } @@ -105,7 +105,7 @@ impl ActionHandler for Logger { } impl Logger { - pub fn new(ui_tx: Sender) -> Self { + pub fn new(ui_tx: Sender) -> Self { Self { ui_tx, logger_state: tui_logger::TuiWidgetState::default(), @@ -115,7 +115,7 @@ impl Logger { async fn handle_view_browser(&mut self) { send_or_error( &self.ui_tx, - UIMessage::ChangeContext(super::WindowContext::Browser), + AppCallback::ChangeContext(super::WindowContext::Browser), ) .await; } diff --git a/src/app/ui/playlist.rs b/src/app/ui/playlist.rs index 457200d2..6943dee3 100644 --- a/src/app/ui/playlist.rs +++ b/src/app/ui/playlist.rs @@ -8,7 +8,7 @@ use crate::app::{ Action, ActionHandler, ActionProcessor, KeyHandler, KeyRouter, Keybind, TextHandler, }, structures::{AlbumSongsList, ListSong, ListSongID, PlayState}, - ui::{UIMessage, WindowContext}, + ui::{AppCallback, WindowContext}, }; use crate::{app::structures::DownloadStatus, core::send_or_error}; @@ -30,7 +30,7 @@ pub struct Playlist { pub cur_played_secs: Option, pub play_status: PlayState, pub volume: Percentage, - ui_tx: mpsc::Sender, + ui_tx: mpsc::Sender, pub help_shown: bool, keybinds: Vec>, } @@ -175,10 +175,10 @@ impl ActionHandler for Playlist { } impl Playlist { - pub fn new(ui_tx: mpsc::Sender) -> Self { + pub fn new(ui_tx: mpsc::Sender) -> Self { // This could fail, made to try send to avoid needing to change function signature to asynchronous. Should change. ui_tx - .try_send(UIMessage::GetVolume) + .try_send(AppCallback::GetVolume) .unwrap_or_else(|e| error!("Error <{e}> received sending Get Volume message")); Playlist { help_shown: false, @@ -199,7 +199,7 @@ impl Playlist { // Ask player for a progress update. if let PlayState::Playing(id) = self.play_status { info!("Tick received - requesting song progress update"); - let _ = self.ui_tx.send(UIMessage::GetProgress(id)).await; + let _ = self.ui_tx.send(AppCallback::GetProgress(id)).await; } } pub async fn handle_song_progress_update( @@ -305,7 +305,7 @@ impl Playlist { if let Some(cur_playing_id) = self.get_cur_playing_id() { if Some(cur_selected_idx) == self.get_cur_playing_index() { self.play_status = PlayState::NotPlaying; - send_or_error(&self.ui_tx, UIMessage::Stop(cur_playing_id)).await; + send_or_error(&self.ui_tx, AppCallback::Stop(cur_playing_id)).await; } } // TODO: Resolve offset commands @@ -319,7 +319,7 @@ impl Playlist { pub async fn view_browser(&mut self) { send_or_error( &self.ui_tx, - UIMessage::ChangeContext(WindowContext::Browser), + AppCallback::ChangeContext(WindowContext::Browser), ) .await; } @@ -354,7 +354,7 @@ impl Playlist { pub async fn reset(&mut self) { // Stop playback, if playing. if let Some(cur_id) = self.get_cur_playing_id() { - send_or_error(&self.ui_tx, UIMessage::Stop(cur_id)).await; + send_or_error(&self.ui_tx, AppCallback::Stop(cur_id)).await; } self.clear() // XXX: Also need to kill pending download tasks @@ -367,7 +367,7 @@ impl Playlist { } pub async fn play_song_id(&mut self, id: ListSongID) { if let Some(cur_id) = self.get_cur_playing_id() { - send_or_error(&self.ui_tx, UIMessage::Stop(cur_id)).await; + send_or_error(&self.ui_tx, AppCallback::Stop(cur_id)).await; } // Drop previous songs self.drop_unscoped_from_id(id); @@ -381,7 +381,7 @@ impl Playlist { .expect("Checked previously") .download_status { - send_or_error(&self.ui_tx, UIMessage::PlaySong(pointer.clone(), id)).await; + send_or_error(&self.ui_tx, AppCallback::PlaySong(pointer.clone(), id)).await; self.play_status = PlayState::Playing(id); } else { self.play_status = PlayState::Buffering(id); @@ -406,7 +406,7 @@ impl Playlist { }; send_or_error( &self.ui_tx, - UIMessage::DownloadSong(song.raw.get_video_id().clone(), id), + AppCallback::DownloadSong(song.raw.get_video_id().clone(), id), ) .await; song.download_status = DownloadStatus::Queued; @@ -435,7 +435,7 @@ impl Playlist { } None => { info!("No next song - finishing playback"); - send_or_error(&self.ui_tx, UIMessage::Stop(*id)).await; + send_or_error(&self.ui_tx, AppCallback::Stop(*id)).await; } } } @@ -528,7 +528,7 @@ impl Playlist { } _ => return, }; - send_or_error(&self.ui_tx, UIMessage::PausePlay(id)).await; + send_or_error(&self.ui_tx, AppCallback::PausePlay(id)).await; } pub fn get_cur_playing_id(&self) -> Option { match self.play_status {