use std::sync::Arc;
use action::Action;
use mecomp_core::{
rpc::{MusicPlayerClient, SearchResult},
state::{library::LibraryFull, StateAudio},
};
use tokio::sync::{
broadcast,
mpsc::{self, UnboundedReceiver, UnboundedSender},
};
use crate::{
termination::{Interrupted, Terminator},
ui::{components::content_view::ActiveView, widgets::popups::PopupType},
};
pub mod action;
pub mod audio;
pub mod component;
pub mod library;
pub mod popup;
pub mod search;
pub mod view;
pub struct Dispatcher {
audio: audio::AudioState,
search: search::SearchState,
library: library::LibraryState,
view: view::ViewState,
popup: popup::PopupState,
component: component::ComponentState,
}
struct Senders {
pub audio: UnboundedSender<action::AudioAction>,
pub search: UnboundedSender<String>,
pub library: UnboundedSender<action::LibraryAction>,
pub view: UnboundedSender<ActiveView>,
pub popup: UnboundedSender<action::PopupAction>,
pub component: UnboundedSender<action::ComponentAction>,
}
pub struct Receivers {
pub audio: UnboundedReceiver<StateAudio>,
pub search: UnboundedReceiver<SearchResult>,
pub library: UnboundedReceiver<LibraryFull>,
pub view: UnboundedReceiver<ActiveView>,
pub popup: UnboundedReceiver<Option<PopupType>>,
pub component: UnboundedReceiver<component::ActiveComponent>,
}
impl Dispatcher {
#[must_use]
pub fn new() -> (Self, Receivers) {
let (audio, audio_rx) = audio::AudioState::new();
let (search, search_rx) = search::SearchState::new();
let (library, library_rx) = library::LibraryState::new();
let (view, view_rx) = view::ViewState::new();
let (popup, popup_rx) = popup::PopupState::new();
let (active_component, active_component_rx) = component::ComponentState::new();
let dispatcher = Self {
audio,
search,
library,
view,
popup,
component: active_component,
};
let state_receivers = Receivers {
audio: audio_rx,
search: search_rx,
library: library_rx,
view: view_rx,
popup: popup_rx,
component: active_component_rx,
};
(dispatcher, state_receivers)
}
pub async fn main_loop(
&self,
daemon: Arc<MusicPlayerClient>,
terminator: Terminator,
action_rx: UnboundedReceiver<Action>,
mut interrupt_rx: broadcast::Receiver<Interrupted>,
) -> anyhow::Result<Interrupted> {
let (audio_action_tx, audio_action_rx) = mpsc::unbounded_channel();
let (search_action_tx, search_action_rx) = mpsc::unbounded_channel();
let (library_action_tx, library_action_rx) = mpsc::unbounded_channel();
let (view_action_tx, view_action_rx) = mpsc::unbounded_channel();
let (popup_action_tx, popup_action_rx) = mpsc::unbounded_channel();
let (component_action_tx, component_action_rx) = mpsc::unbounded_channel();
tokio::try_join!(
self.audio
.main_loop(daemon.clone(), audio_action_rx, interrupt_rx.resubscribe()),
self.search
.main_loop(daemon.clone(), search_action_rx, interrupt_rx.resubscribe()),
self.library.main_loop(
daemon.clone(),
library_action_rx,
interrupt_rx.resubscribe()
),
self.view
.main_loop(view_action_rx, interrupt_rx.resubscribe()),
self.popup
.main_loop(popup_action_rx, interrupt_rx.resubscribe()),
self.component
.main_loop(component_action_rx, interrupt_rx.resubscribe()),
Self::action_dispatcher(
terminator,
action_rx,
Senders {
audio: audio_action_tx,
search: search_action_tx,
library: library_action_tx,
view: view_action_tx,
popup: popup_action_tx,
component: component_action_tx,
},
),
)?;
Ok(interrupt_rx.recv().await?)
}
async fn action_dispatcher(
mut terminator: Terminator,
mut action_rx: UnboundedReceiver<Action>,
senders: Senders,
) -> anyhow::Result<()> {
while let Some(action) = action_rx.recv().await {
match action {
Action::Audio(action) => {
senders.audio.send(action)?;
}
Action::Search(query) => {
senders.search.send(query)?;
}
Action::General(action) => match action {
action::GeneralAction::Exit => {
let _ = terminator.terminate(Interrupted::UserInt);
break;
}
},
Action::Library(action) => {
senders.library.send(action)?;
}
Action::SetCurrentView(view) => {
senders.view.send(view)?;
}
Action::Popup(popup) => senders.popup.send(popup)?,
Action::ActiveComponent(action) => {
senders.component.send(action)?;
}
}
}
Ok(())
}
}