diff --git a/hammond-data/src/dbqueries.rs b/hammond-data/src/dbqueries.rs index 04bdcdc..4923310 100644 --- a/hammond-data/src/dbqueries.rs +++ b/hammond-data/src/dbqueries.rs @@ -33,6 +33,18 @@ pub fn get_podcasts() -> Result, DataError> { .map_err(From::from) } +pub fn get_podcasts_filter(filter_ids: &[i32]) -> Result, DataError> { + use schema::podcast::dsl::*; + let db = connection(); + let con = db.get()?; + + podcast + .order(title.asc()) + .filter(id.ne_any(filter_ids)) + .load::(&con) + .map_err(From::from) +} + pub fn get_episodes() -> Result, DataError> { use schema::episode::dsl::*; let db = connection(); @@ -102,7 +114,10 @@ pub fn get_episode_local_uri_from_id(ep_id: i32) -> Result, DataE .map_err(From::from) } -pub fn get_episodes_widgets_with_limit(limit: u32) -> Result, DataError> { +pub fn get_episodes_widgets_filter_limit( + filter_ids: &[i32], + limit: u32, +) -> Result, DataError> { use schema::episode; let db = connection(); let con = db.get()?; @@ -120,6 +135,7 @@ pub fn get_episodes_widgets_with_limit(limit: u32) -> Result(&con) .map_err(From::from) diff --git a/hammond-gtk/src/app.rs b/hammond-gtk/src/app.rs index c7ae2a5..75dbf6d 100644 --- a/hammond-gtk/src/app.rs +++ b/hammond-gtk/src/app.rs @@ -7,6 +7,7 @@ use gtk::SettingsExt as GtkSettingsExt; use gtk::prelude::*; use hammond_data::{Podcast, Source}; +use hammond_data::utils::delete_show; use appnotif::*; use headerbar::Header; @@ -16,6 +17,7 @@ use widgets::mark_all_watched; use std::sync::Arc; use std::sync::mpsc::{channel, Receiver, Sender}; +use std::thread; use std::time::Duration; #[derive(Clone, Debug)] @@ -36,6 +38,7 @@ pub enum Action { HeaderBarShowUpdateIndicator, HeaderBarHideUpdateIndicator, MarkAllPlayerNotification(Arc), + RemoveShow(Arc), } #[derive(Debug)] @@ -192,7 +195,53 @@ impl App { }); let text = "Marked all episodes as listened"; - let notif = InAppNotification::new(text.into(), callback, sender.clone()); + let notif = + InAppNotification::new(text.into(), callback, || {}, sender.clone()); + overlay.add_overlay(¬if.revealer); + // We need to display the notification after the widget is added to the overlay + // so there will be a nice animation. + notif.show(); + } + Ok(Action::RemoveShow(pd)) => { + if let Err(err) = utils::ignore_show(pd.id()) { + error!("Could not insert {} to the ignore list.", pd.title()); + error!("Error: {}", err); + } + + let callback = clone!(pd => move || { + // Spawn a thread so it won't block the ui. + thread::spawn(clone!(pd => move || { + if let Err(err) = delete_show(&pd) { + error!("Something went wrong trying to remove {}", pd.title()); + error!("Error: {}", err); + } + })); + glib::Continue(false) + }); + + let undo_callback = clone!(pd, sender => move || { + if let Err(err) = utils::uningore_show(pd.id()) { + error!("Could not insert {} to the ignore list.", pd.title()); + error!("Error: {}", err); + } + + + if let Err(err) = sender.send(Action::RefreshShowsView) { + error!("Action channl blew up, error {}", err) + } + + if let Err(err) = sender.send(Action::RefreshEpisodesView) { + error!("Action channl blew up, error {}", err) + } + }); + + let text = format!("Unsubscribed from {}", pd.title()); + let notif = InAppNotification::new( + text.into(), + callback, + undo_callback, + sender.clone(), + ); overlay.add_overlay(¬if.revealer); // We need to display the notification after the widget is added to the overlay // so there will be a nice animation. diff --git a/hammond-gtk/src/appnotif.rs b/hammond-gtk/src/appnotif.rs index 07291a7..67168c1 100644 --- a/hammond-gtk/src/appnotif.rs +++ b/hammond-gtk/src/appnotif.rs @@ -35,9 +35,15 @@ impl Default for InAppNotification { } impl InAppNotification { - pub fn new(text: String, mut callback: F, sender: Sender) -> Self + pub fn new( + text: String, + mut callback: F, + undo_callback: U, + sender: Sender, + ) -> Self where F: FnMut() -> glib::Continue + 'static, + U: Fn() + 'static, { let notif = InAppNotification::default(); notif.text.set_text(&text); @@ -57,14 +63,13 @@ impl InAppNotification { glib::source::source_remove(id); } + undo_callback(); + // Hide the notification revealer.set_reveal_child(false); // Refresh the widget if visible if let Err(err) = sender.send(Action::RefreshWidgetIfVis) { - error!( - "Something went horribly wrong with the Action channel: {}", - err - ) + error!("Action channel blew up: {}", err) } }); diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index e67051f..f8a6bcb 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -15,8 +15,9 @@ use hammond_data::pipeline; use hammond_data::utils::checkup; use hammond_downloader::downloader; -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::sync::{Mutex, RwLock}; +use std::sync::Arc; use std::sync::mpsc::Sender; use std::thread; @@ -25,6 +26,28 @@ use app::Action; use chrono::Duration; use chrono::prelude::*; +lazy_static! { + static ref IGNORESHOWS: Arc>> = Arc::new(Mutex::new(HashSet::new())); +} + +pub fn ignore_show(id: i32) -> Result<(), Error> { + let mut guard = IGNORESHOWS.lock().map_err(|err| format_err!("{}", err))?; + guard.insert(id); + Ok(()) +} + +pub fn uningore_show(id: i32) -> Result<(), Error> { + let mut guard = IGNORESHOWS.lock().map_err(|err| format_err!("{}", err))?; + guard.remove(&id); + Ok(()) +} + +pub fn get_ignored_shows() -> Result, Error> { + let guard = IGNORESHOWS.lock().map_err(|err| format_err!("{}", err))?; + let keys = guard.iter().cloned().collect::>(); + Ok(keys) +} + pub fn cleanup(cleanup_date: DateTime) { if let Err(err) = checkup(cleanup_date) { error!("Check up failed: {}", err); diff --git a/hammond-gtk/src/views/episodes.rs b/hammond-gtk/src/views/episodes.rs index 09c623a..43b9bf7 100644 --- a/hammond-gtk/src/views/episodes.rs +++ b/hammond-gtk/src/views/episodes.rs @@ -7,7 +7,7 @@ use hammond_data::EpisodeWidgetQuery; use hammond_data::dbqueries; use app::Action; -use utils::get_pixbuf_from_path; +use utils::{get_ignored_shows, get_pixbuf_from_path}; use widgets::EpisodeWidget; use std::sync::mpsc::Sender; @@ -77,7 +77,8 @@ impl Default for EpisodesView { impl EpisodesView { pub fn new(sender: Sender) -> Result { let view = EpisodesView::default(); - let episodes = dbqueries::get_episodes_widgets_with_limit(50)?; + let ignore = get_ignored_shows()?; + let episodes = dbqueries::get_episodes_widgets_filter_limit(&ignore, 50)?; let now_utc = Utc::now(); episodes.into_iter().for_each(|ep| { diff --git a/hammond-gtk/src/views/shows.rs b/hammond-gtk/src/views/shows.rs index 38dfb0d..2a59984 100644 --- a/hammond-gtk/src/views/shows.rs +++ b/hammond-gtk/src/views/shows.rs @@ -6,7 +6,7 @@ use hammond_data::Podcast; use hammond_data::dbqueries; use app::Action; -use utils::get_pixbuf_from_path; +use utils::{get_ignored_shows, get_pixbuf_from_path}; use std::sync::Arc; use std::sync::mpsc::Sender; @@ -54,7 +54,8 @@ impl ShowsPopulated { } fn populate_flowbox(&self) -> Result<(), Error> { - let podcasts = dbqueries::get_podcasts()?; + let ignore = get_ignored_shows()?; + let podcasts = dbqueries::get_podcasts_filter(&ignore)?; podcasts.into_iter().map(Arc::new).for_each(|parent| { let flowbox_child = ShowsChild::new(parent); diff --git a/hammond-gtk/src/widgets/show.rs b/hammond-gtk/src/widgets/show.rs index abe9a37..5a304fe 100644 --- a/hammond-gtk/src/widgets/show.rs +++ b/hammond-gtk/src/widgets/show.rs @@ -7,7 +7,7 @@ use open; use hammond_data::Podcast; use hammond_data::dbqueries; -use hammond_data::utils::{delete_show, replace_extra_spaces}; +use hammond_data::utils::replace_extra_spaces; use app::Action; use utils::get_pixbuf_from_path; @@ -15,7 +15,6 @@ use widgets::episode::episodes_listbox; use std::sync::Arc; use std::sync::mpsc::Sender; -use std::thread; #[derive(Debug, Clone)] pub struct ShowWidget { @@ -141,13 +140,7 @@ fn on_unsub_button_clicked( // hack to get away without properly checking for none. // if pressed twice would panic. unsub_button.hide(); - // Spawn a thread so it won't block the ui. - thread::spawn(move || { - if let Err(err) = delete_show(&pd) { - error!("Something went wrong trying to remove {}", pd.title()); - error!("Error: {}", err); - } - }); + sender.send(Action::RemoveShow(pd))?; sender.send(Action::HeaderBarNormal)?; sender.send(Action::ShowShowsAnimated)?;