use std::
{
sync::Arc,
time::Instant,
};
use crossterm::event::KeyEventKind;
use tokio::
{
net::tcp::OwnedWriteHalf,
sync::
{
Mutex as MutexAsync,
mpsc::Sender,
},
};
use crate::
{
t,
network::
{
client::ClientEvent,
voice::client::message,
},
};
use super::
{
consts,
state::
{
App,
Playback,
Recording,
},
};
fn start(app: &mut App, tx: &Sender<ClientEvent>)
{
if !message::start_recording(tx.clone())
{
app.notify(t!("voice_message.busy"));
return;
}
let now = Instant::now();
app.recording = Some(Recording { started: now, pressed: now, held: app.key_release, shown: 0 });
app.dirty = true;
}
fn send(app: &mut App, write_stream: Option<&Arc<MutexAsync<OwnedWriteHalf>>>, tx: &Sender<ClientEvent>)
{
app.recording = None;
app.dirty = true;
let Some(write_stream) = write_stream else { return message::cancel_recording() };
match message::finish_recording()
{
Some(clip) => crate::send_voice(write_stream, clip, tx.clone()),
None => app.notify(t!("voice_message.too_short")),
}
}
pub fn press(app: &mut App, kind: KeyEventKind, write_stream: &Arc<MutexAsync<OwnedWriteHalf>>, tx: &Sender<ClientEvent>)
{
let now = Instant::now();
let Some(recording) = app.recording.as_mut() else { return start(app, tx) };
if kind == KeyEventKind::Repeat || (!app.key_release && now - recording.pressed < consts::KEY_REPEAT_GAP)
{
if !app.key_release { recording.held = true; }
recording.pressed = now;
return;
}
send(app, Some(write_stream), tx);
}
pub fn release(app: &mut App, write_stream: Option<&Arc<MutexAsync<OwnedWriteHalf>>>, tx: &Sender<ClientEvent>)
{
let Some(recording) = app.recording.as_mut().filter(|recording| recording.held) else { return };
if recording.started.elapsed() < consts::KEY_TAP
{
recording.held = false;
app.dirty = true;
return;
}
send(app, write_stream, tx);
}
pub fn toggle(app: &mut App, write_stream: &Arc<MutexAsync<OwnedWriteHalf>>, tx: &Sender<ClientEvent>)
{
match app.recording
{
Some(_) => send(app, Some(write_stream), tx),
None =>
{
start(app, tx);
if let Some(recording) = app.recording.as_mut() { recording.held = false; }
},
}
}
pub fn cancel(app: &mut App) {
message::cancel_recording();
app.recording = None;
app.notify(t!("voice_message.discarded"));
}
pub fn tick(app: &mut App, write_stream: Option<&Arc<MutexAsync<OwnedWriteHalf>>>, tx: &Sender<ClientEvent>)
{
app.sync_playing(message::playing());
let Some(recording) = app.recording.as_mut() else { return };
let Some((recorded, full)) = message::recording() else
{
app.recording = None;
app.dirty = true;
return;
};
if full
{
app.notify(t!("voice_message.full"));
return send(app, write_stream, tx);
}
if !app.key_release && recording.held && recording.pressed.elapsed() > consts::KEY_RELEASE_GAP
{
return send(app, write_stream, tx);
}
let seconds = recorded / 1000;
if seconds != recording.shown
{
recording.shown = seconds;
app.dirty = true;
}
}
pub fn status(app: &App) -> Option<String>
{
let recording = app.recording?;
let duration = format!("{}:{:02}", recording.shown / 60, recording.shown % 60);
let send = if recording.held { t!("voice_message.hold") } else { t!("voice_message.toggle") };
Some(format!("{} · {send} · {}", t!("voice_message.recording", duration), t!("voice_message.cancel")))
}
pub fn play(app: &mut App, hash: [u8; 32], tx: &Sender<ClientEvent>)
{
app.stop_loading();
app.set_playback(&hash, Playback::Loading);
message::play(hash, tx.clone());
}
pub fn stop(app: &mut App) {
message::stop();
app.stop_loading();
app.sync_playing(None);
}
pub fn click(app: &mut App, entry: usize, tx: &Sender<ClientEvent>)
{
let Some((hash, active)) = app.voice_entry(entry) else { return };
match active
{
true => stop(app),
false => play(app, hash, tx),
}
}