use std::sync::{Arc, RwLock}; use iced::{ Alignment::Center, Element, Task, Theme, widget::{button, column, row}, }; use protocol::BUFFER_LENGTH; use tokio::sync::{ broadcast::{self}, oneshot, }; use crate::{ClientConfig, stream::connect, voice::record}; #[derive(Debug, Default)] struct Signal { microphone: Option>, // speaker: Option>, connection: Option>, } #[derive(Debug)] struct Channel { microphone: broadcast::Sender, // speaker: (broadcast::Sender, broadcast::Receiver), } impl Channel { fn new() -> Self { Self { microphone: broadcast::channel(BUFFER_LENGTH).0, // speaker: broadcast::channel(BUFFER_LENGTH), } } } #[derive(Debug, Default, Clone, Copy)] struct GUIStatus { room: State, microphone: State, } #[derive(Debug, Clone, Copy)] pub enum State { Active, Passive, Loading, } impl Default for State { fn default() -> Self { State::Passive } } #[derive(Debug, Clone, Copy)] pub enum Message { State, JoinRoom, LeaveRoom, UnmuteMicrophone, MuteMicrophone, } #[derive(Debug)] pub struct App { client_config: Arc, gui_status: Arc>, channel: Channel, signal: Signal, } impl App { pub fn theme(&self) -> Theme { Theme::Dark } pub fn new() -> Self { App { client_config: ClientConfig::new().into(), gui_status: RwLock::new(GUIStatus::default()).into(), channel: Channel::new(), signal: Signal::default(), } } pub fn view(&self) -> Element<'_, Message> { let join_room_button = match self.gui_status.read().unwrap().room { State::Active => button("Leave Room").on_press(Message::LeaveRoom), State::Passive => button("Join Room").on_press(Message::JoinRoom), State::Loading => button("Loading"), }; let microphone_button = match self.gui_status.read().unwrap().microphone { State::Active => button("Mute").on_press(Message::MuteMicrophone), State::Passive => button("Unmute").on_press(Message::UnmuteMicrophone), State::Loading => button("Loading"), }; column![ row![join_room_button, microphone_button] .spacing(20) .align_y(Center) ] .align_x(Center) .into() } pub fn update(&mut self, message: Message) -> Task { match message { Message::State => Task::none(), Message::JoinRoom => { self.gui_status.write().unwrap().room = State::Loading; let client_config = self.client_config.clone(); let gui_status = self.gui_status.clone(); let microphone_receiver = self.channel.microphone.subscribe(); let connection_signal = oneshot::channel(); self.signal.connection = Some(connection_signal.0); Task::perform( connect(connection_signal.1, microphone_receiver, client_config), move |result| match result { Ok(_) => gui_status.write().unwrap().room = State::Active, Err(err_val) => { eprintln!("Error: Join Room | {}", err_val); gui_status.write().unwrap().room = State::Passive; } }, ) .map(|_| Message::State) } Message::LeaveRoom => { self.gui_status.write().unwrap().room = State::Loading; if let Some(connection_signal) = &self.signal.connection { if !connection_signal.is_closed() { self.signal .connection .take() .expect("Never") .send(true) .unwrap(); self.signal.connection = None; self.gui_status.write().unwrap().room = State::Passive; } } Task::none() } Message::UnmuteMicrophone => { self.gui_status.write().unwrap().microphone = State::Active; let microphone_sender = self.channel.microphone.clone(); let microphone_stop_signal = oneshot::channel(); self.signal.microphone = Some(microphone_stop_signal.0); Task::perform(record(microphone_sender, microphone_stop_signal.1), |_| { Message::State }) } Message::MuteMicrophone => { self.gui_status.write().unwrap().microphone = State::Loading; if let Some(microphone_signal) = &self.signal.microphone { if !microphone_signal.is_closed() { self.signal .microphone .take() .expect("Never") .send(true) .unwrap(); self.signal.microphone = None; self.gui_status.write().unwrap().microphone = State::Passive; } } Task::none() } } } }