feat: make loading indicator have optional progress

This commit is contained in:
talwat 2025-12-02 15:30:48 +01:00
parent 1f3a751a90
commit f8b39da92f
4 changed files with 32 additions and 30 deletions

View File

@ -2,39 +2,35 @@ use std::{sync::Arc, thread::sleep, time::Duration};
use rodio::Sink;
use tokio::{
sync::{broadcast, mpsc},
sync::{mpsc, Notify},
task::{self, JoinHandle},
};
use crate::ui::{self, Update};
pub struct Handle {
_task: JoinHandle<()>,
notify: Arc<Notify>,
}
impl Handle {
pub fn new(
sink: Arc<Sink>,
tx: mpsc::Sender<crate::Message>,
rx: broadcast::Receiver<ui::Update>,
) -> Self {
pub fn new(sink: Arc<Sink>, tx: mpsc::Sender<crate::Message>) -> Self {
let notify = Arc::new(Notify::new());
Self {
_task: task::spawn_blocking(|| Self::waiter(sink, tx, rx)),
_task: task::spawn(Self::waiter(sink, tx, notify.clone())),
notify,
}
}
fn waiter(
sink: Arc<Sink>,
tx: mpsc::Sender<crate::Message>,
mut rx: broadcast::Receiver<ui::Update>,
) {
pub fn notify(&self) {
self.notify.notify_one();
}
async fn waiter(sink: Arc<Sink>, tx: mpsc::Sender<crate::Message>, notify: Arc<Notify>) {
'main: loop {
if !matches!(rx.blocking_recv(), Ok(Update::Track(_))) {
continue;
}
notify.notified().await;
while !sink.empty() {
if matches!(rx.try_recv(), Ok(Update::Quit)) {
if Arc::strong_count(&notify) <= 1 {
break 'main;
}

View File

@ -78,7 +78,7 @@ pub struct Handle {
}
pub enum Output {
Loading(Progress),
Loading(Option<Progress>),
Queued(tracks::Queued),
}
@ -88,7 +88,7 @@ impl Handle {
Ok(queued) => Output::Queued(queued),
Err(_) => {
PROGRESS.store(0, atomic::Ordering::Relaxed);
Output::Loading(progress())
Output::Loading(Some(progress()))
}
}
}

View File

@ -17,10 +17,16 @@ use crate::{
#[derive(Clone, Debug)]
pub enum Current {
Loading(download::Progress),
Loading(Option<download::Progress>),
Track(tracks::Info),
}
impl Default for Current {
fn default() -> Self {
Current::Loading(None)
}
}
impl Current {
pub fn loading(&self) -> bool {
return matches!(self, Current::Loading(_));
@ -35,15 +41,14 @@ pub struct Player {
broadcast: broadcast::Sender<ui::Update>,
current: Current,
ui: ui::Handle,
waiter: waiter::Handle,
_tx: Sender<crate::Message>,
_waiter: waiter::Handle,
_stream: rodio::OutputStream,
}
impl Drop for Player {
fn drop(&mut self) {
self.sink.stop();
self.broadcast.send(ui::Update::Quit).unwrap();
}
}
@ -82,7 +87,7 @@ impl Player {
let (tx, rx) = mpsc::channel(8);
tx.send(Message::Init).await?;
let (utx, urx) = broadcast::channel(8);
let current = Current::Loading(download::progress());
let current = Current::Loading(None);
let list = List::load(args.track_list.as_ref()).await?;
let state = ui::State::initial(sink.clone(), &args, current.clone(), list.name.clone());
@ -92,8 +97,8 @@ impl Player {
Ok(Self {
downloader: Downloader::init(args.buffer_size, list, tx.clone()).await,
ui: ui::Handle::init(tx.clone(), urx.resubscribe(), state.clone(), &args).await?,
_waiter: waiter::Handle::new(sink.clone(), tx.clone(), urx),
ui: ui::Handle::init(tx.clone(), urx, state, &args).await?,
waiter: waiter::Handle::new(sink.clone(), tx.clone()),
bookmarks: Bookmarks::load().await?,
current,
broadcast: utx,
@ -115,6 +120,7 @@ impl Player {
let decoded = queued.decode()?;
self.sink.append(decoded.data);
self.set_current(Current::Track(decoded.info)).await?;
self.waiter.notify();
Ok(())
}

View File

@ -66,7 +66,7 @@ enum ActionBar {
Playing(tracks::Info),
/// When the app is loading.
Loading(u8),
Loading(Option<u8>),
/// When the app is muted.
Muted,
@ -80,9 +80,9 @@ impl ActionBar {
Self::Playing(x) => ("playing", Some((x.display.clone(), x.width))),
Self::Paused(x) => ("paused", Some((x.display.clone(), x.width))),
Self::Loading(progress) => {
let progress = format!("{: <2.0}%", progress.min(&99));
let progress = progress.map(|progress| (format!("{: <2.0}%", progress.min(99)), 3));
("loading", Some((progress, 3)))
("loading", progress)
}
Self::Muted => {
let msg = "+ to increase volume";
@ -108,7 +108,7 @@ impl ActionBar {
pub fn action(state: &ui::State, width: usize) -> String {
let action = match state.current.clone() {
Current::Loading(progress) => {
ActionBar::Loading(progress.load(std::sync::atomic::Ordering::Relaxed))
ActionBar::Loading(progress.map(|x| x.load(std::sync::atomic::Ordering::Relaxed)))
}
Current::Track(info) => {
if state.sink.volume() < 0.01 {