feat: ✨ talk and listen on client side
This commit is contained in:
parent
1567a9c32a
commit
1e9808579a
11 changed files with 388 additions and 79 deletions
|
@ -1,42 +1,172 @@
|
|||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use iced::{
|
||||
Alignment::Center,
|
||||
Element, Task, Theme,
|
||||
widget::{button, column, text},
|
||||
widget::{button, column, row},
|
||||
};
|
||||
use protocol::BUFFER_LENGTH;
|
||||
use tokio::sync::{
|
||||
broadcast::{self},
|
||||
oneshot,
|
||||
};
|
||||
|
||||
use crate::{ClientConfig, stream::connect};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
JoinRoom,
|
||||
LeaveRoom,
|
||||
MuteMicrophone,
|
||||
UnmuteMicrophone,
|
||||
}
|
||||
use crate::{ClientConfig, stream::connect, voice::record};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Data {
|
||||
client_config: Option<ClientConfig>,
|
||||
struct Signal {
|
||||
microphone: Option<oneshot::Sender<bool>>,
|
||||
// speaker: Option<oneshot::Sender<bool>>,
|
||||
connection: Option<oneshot::Sender<bool>>,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub fn update(&mut self, message: Message) -> Task<Message> {
|
||||
match message {
|
||||
Message::JoinRoom => Task::none(),
|
||||
Message::LeaveRoom => Task::none(),
|
||||
Message::MuteMicrophone => Task::none(),
|
||||
Message::UnmuteMicrophone => Task::none(),
|
||||
#[derive(Debug)]
|
||||
struct Channel {
|
||||
microphone: broadcast::Sender<f32>,
|
||||
// speaker: (broadcast::Sender<f32>, broadcast::Receiver<f32>),
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
microphone: broadcast::channel(BUFFER_LENGTH).0,
|
||||
// speaker: broadcast::channel(BUFFER_LENGTH),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view(&self) -> Element<'_, Message> {
|
||||
column![button("Join Room"), text("Hello").size(50)]
|
||||
.padding(10)
|
||||
.align_x(Center)
|
||||
.into()
|
||||
#[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<ClientConfig>,
|
||||
gui_status: Arc<RwLock<GUIStatus>>,
|
||||
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<Message> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue