diff --git a/psst-gui/src/controller/alert_cleanup.rs b/psst-gui/src/controller/alert_cleanup.rs new file mode 100644 index 00000000..3f47faf3 --- /dev/null +++ b/psst-gui/src/controller/alert_cleanup.rs @@ -0,0 +1,30 @@ +use crate::data::AppState; +use druid::{widget::Controller, Env, Event, EventCtx, Widget}; +use std::time::Duration; + +pub struct AlertCleanupController; + +const CLEANUP_INTERVAL: Duration = Duration::from_secs(1); + +impl> Controller for AlertCleanupController { + fn event( + &mut self, + child: &mut W, + ctx: &mut EventCtx, + event: &Event, + data: &mut AppState, + env: &Env, + ) { + match event { + Event::WindowConnected => { + ctx.request_timer(CLEANUP_INTERVAL); + } + Event::Timer(_) => { + data.cleanup_alerts(); + ctx.request_timer(CLEANUP_INTERVAL); + } + _ => {} + } + child.event(ctx, event, data, env) + } +} diff --git a/psst-gui/src/controller/mod.rs b/psst-gui/src/controller/mod.rs index 657e23d9..ea3dc830 100644 --- a/psst-gui/src/controller/mod.rs +++ b/psst-gui/src/controller/mod.rs @@ -1,4 +1,5 @@ mod after_delay; +mod alert_cleanup; mod ex_click; mod ex_cursor; mod ex_scroll; @@ -13,6 +14,7 @@ mod session; mod sort; pub use after_delay::AfterDelay; +pub use alert_cleanup::AlertCleanupController; pub use ex_click::ExClick; pub use ex_cursor::ExCursor; pub use ex_scroll::ExScroll; diff --git a/psst-gui/src/data/mod.rs b/psst-gui/src/data/mod.rs index 26b2a13e..2c1c33fe 100644 --- a/psst-gui/src/data/mod.rs +++ b/psst-gui/src/data/mod.rs @@ -23,7 +23,7 @@ use std::{ atomic::{AtomicUsize, Ordering}, Arc, }, - time::Duration, + time::{Duration, Instant}, }; use druid::{ @@ -60,6 +60,8 @@ pub use crate::data::{ utils::{Cached, Float64, Image, Page}, }; +pub const ALERT_DURATION: Duration = Duration::from_secs(5); + #[derive(Clone, Data, Lens)] pub struct AppState { #[data(ignore)] @@ -276,25 +278,33 @@ impl AppState { } impl AppState { - pub fn info_alert(&mut self, message: impl Display) { - self.alerts.push_back(Alert { + pub fn add_alert(&mut self, message: impl Display, style: AlertStyle) { + let alert = Alert { message: message.to_string().into(), - style: AlertStyle::Info, + style, id: Alert::fresh_id(), - }); + created_at: Instant::now(), + }; + self.alerts.push_back(alert); + } + + pub fn info_alert(&mut self, message: impl Display) { + self.add_alert(message, AlertStyle::Info); } pub fn error_alert(&mut self, message: impl Display) { - self.alerts.push_back(Alert { - message: message.to_string().into(), - style: AlertStyle::Error, - id: Alert::fresh_id(), - }); + self.add_alert(message, AlertStyle::Error); } pub fn dismiss_alert(&mut self, id: usize) { self.alerts.retain(|a| a.id != id); } + + pub fn cleanup_alerts(&mut self) { + let now = Instant::now(); + self.alerts + .retain(|alert| now.duration_since(alert.created_at) < ALERT_DURATION); + } } #[derive(Clone, Data, Lens)] @@ -512,6 +522,7 @@ pub struct Alert { pub id: usize, pub message: Arc, pub style: AlertStyle, + pub created_at: Instant, } impl Alert { diff --git a/psst-gui/src/ui/mod.rs b/psst-gui/src/ui/mod.rs index 2058ee19..347607bc 100644 --- a/psst-gui/src/ui/mod.rs +++ b/psst-gui/src/ui/mod.rs @@ -10,9 +10,12 @@ use druid_shell::Cursor; use crate::data::config::SortCriteria; use crate::{ cmd, - controller::{AfterDelay, NavController, SessionController, SortController}, + controller::{ + AfterDelay, AlertCleanupController, NavController, SessionController, SortController, + }, data::{ config::SortOrder, Alert, AlertStyle, AppState, Config, Nav, Playable, Playback, Route, + ALERT_DURATION, }, widget::{ icons, icons::SvgIcon, Border, Empty, MyWidgetExt, Overlay, ThemeScope, ViewDispatcher, @@ -170,7 +173,6 @@ fn root_widget() -> impl Widget { fn alert_widget() -> impl Widget { const BG: Key = Key::new("app.alert.BG"); const DISMISS_ALERT: Selector = Selector::new("app.alert.dismiss"); - const ALERT_DURATION: Duration = Duration::from_secs(5); List::new(|| { Flex::row() @@ -205,6 +207,7 @@ fn alert_widget() -> impl Widget { .on_command(DISMISS_ALERT, |_, &id, state| { state.dismiss_alert(id); }) + .controller(AlertCleanupController) } fn route_widget() -> impl Widget { diff --git a/psst-gui/src/ui/playback.rs b/psst-gui/src/ui/playback.rs index 441850ee..aafae069 100644 --- a/psst-gui/src/ui/playback.rs +++ b/psst-gui/src/ui/playback.rs @@ -108,9 +108,12 @@ fn playing_item_widget() -> impl Widget { ctx.submit_command(cmd::NAVIGATE.with(now_playing.origin.to_nav())); }) .context_menu(|now_playing| match &now_playing.item { - Playable::Track(track) => { - track::track_menu(track, &now_playing.library, &now_playing.origin, usize::MAX) - } + Playable::Track(track) => track::track_menu( + track, + &now_playing.library, + &now_playing.origin, + usize::MAX, + ), Playable::Episode(episode) => { episode::episode_menu(episode, &now_playing.library) }