Merge branch '36-add-undo-for-unsubscribing-from-shows' into 'master'
Resolve "Add "undo" for unsubscribing from shows" Closes #36 See merge request alatiera/Hammond!27
This commit is contained in:
commit
3d39638c99
@ -33,6 +33,18 @@ pub fn get_podcasts() -> Result<Vec<Podcast>, DataError> {
|
|||||||
.map_err(From::from)
|
.map_err(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_podcasts_filter(filter_ids: &[i32]) -> Result<Vec<Podcast>, DataError> {
|
||||||
|
use schema::podcast::dsl::*;
|
||||||
|
let db = connection();
|
||||||
|
let con = db.get()?;
|
||||||
|
|
||||||
|
podcast
|
||||||
|
.order(title.asc())
|
||||||
|
.filter(id.ne_any(filter_ids))
|
||||||
|
.load::<Podcast>(&con)
|
||||||
|
.map_err(From::from)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_episodes() -> Result<Vec<Episode>, DataError> {
|
pub fn get_episodes() -> Result<Vec<Episode>, DataError> {
|
||||||
use schema::episode::dsl::*;
|
use schema::episode::dsl::*;
|
||||||
let db = connection();
|
let db = connection();
|
||||||
@ -102,7 +114,10 @@ pub fn get_episode_local_uri_from_id(ep_id: i32) -> Result<Option<String>, DataE
|
|||||||
.map_err(From::from)
|
.map_err(From::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_episodes_widgets_with_limit(limit: u32) -> Result<Vec<EpisodeWidgetQuery>, DataError> {
|
pub fn get_episodes_widgets_filter_limit(
|
||||||
|
filter_ids: &[i32],
|
||||||
|
limit: u32,
|
||||||
|
) -> Result<Vec<EpisodeWidgetQuery>, DataError> {
|
||||||
use schema::episode;
|
use schema::episode;
|
||||||
let db = connection();
|
let db = connection();
|
||||||
let con = db.get()?;
|
let con = db.get()?;
|
||||||
@ -120,6 +135,7 @@ pub fn get_episodes_widgets_with_limit(limit: u32) -> Result<Vec<EpisodeWidgetQu
|
|||||||
episode::podcast_id,
|
episode::podcast_id,
|
||||||
))
|
))
|
||||||
.order(episode::epoch.desc())
|
.order(episode::epoch.desc())
|
||||||
|
.filter(episode::podcast_id.ne_any(filter_ids))
|
||||||
.limit(i64::from(limit))
|
.limit(i64::from(limit))
|
||||||
.load::<EpisodeWidgetQuery>(&con)
|
.load::<EpisodeWidgetQuery>(&con)
|
||||||
.map_err(From::from)
|
.map_err(From::from)
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use gtk::SettingsExt as GtkSettingsExt;
|
|||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
|
|
||||||
use hammond_data::{Podcast, Source};
|
use hammond_data::{Podcast, Source};
|
||||||
|
use hammond_data::utils::delete_show;
|
||||||
|
|
||||||
use appnotif::*;
|
use appnotif::*;
|
||||||
use headerbar::Header;
|
use headerbar::Header;
|
||||||
@ -16,6 +17,7 @@ use widgets::mark_all_watched;
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||||
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -36,6 +38,7 @@ pub enum Action {
|
|||||||
HeaderBarShowUpdateIndicator,
|
HeaderBarShowUpdateIndicator,
|
||||||
HeaderBarHideUpdateIndicator,
|
HeaderBarHideUpdateIndicator,
|
||||||
MarkAllPlayerNotification(Arc<Podcast>),
|
MarkAllPlayerNotification(Arc<Podcast>),
|
||||||
|
RemoveShow(Arc<Podcast>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -192,7 +195,53 @@ impl App {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let text = "Marked all episodes as listened";
|
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);
|
overlay.add_overlay(¬if.revealer);
|
||||||
// We need to display the notification after the widget is added to the overlay
|
// We need to display the notification after the widget is added to the overlay
|
||||||
// so there will be a nice animation.
|
// so there will be a nice animation.
|
||||||
|
|||||||
@ -35,9 +35,15 @@ impl Default for InAppNotification {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl InAppNotification {
|
impl InAppNotification {
|
||||||
pub fn new<F>(text: String, mut callback: F, sender: Sender<Action>) -> Self
|
pub fn new<F, U>(
|
||||||
|
text: String,
|
||||||
|
mut callback: F,
|
||||||
|
undo_callback: U,
|
||||||
|
sender: Sender<Action>,
|
||||||
|
) -> Self
|
||||||
where
|
where
|
||||||
F: FnMut() -> glib::Continue + 'static,
|
F: FnMut() -> glib::Continue + 'static,
|
||||||
|
U: Fn() + 'static,
|
||||||
{
|
{
|
||||||
let notif = InAppNotification::default();
|
let notif = InAppNotification::default();
|
||||||
notif.text.set_text(&text);
|
notif.text.set_text(&text);
|
||||||
@ -57,14 +63,13 @@ impl InAppNotification {
|
|||||||
glib::source::source_remove(id);
|
glib::source::source_remove(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
undo_callback();
|
||||||
|
|
||||||
// Hide the notification
|
// Hide the notification
|
||||||
revealer.set_reveal_child(false);
|
revealer.set_reveal_child(false);
|
||||||
// Refresh the widget if visible
|
// Refresh the widget if visible
|
||||||
if let Err(err) = sender.send(Action::RefreshWidgetIfVis) {
|
if let Err(err) = sender.send(Action::RefreshWidgetIfVis) {
|
||||||
error!(
|
error!("Action channel blew up: {}", err)
|
||||||
"Something went horribly wrong with the Action channel: {}",
|
|
||||||
err
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -15,8 +15,9 @@ use hammond_data::pipeline;
|
|||||||
use hammond_data::utils::checkup;
|
use hammond_data::utils::checkup;
|
||||||
use hammond_downloader::downloader;
|
use hammond_downloader::downloader;
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::{Mutex, RwLock};
|
use std::sync::{Mutex, RwLock};
|
||||||
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
@ -25,6 +26,28 @@ use app::Action;
|
|||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref IGNORESHOWS: Arc<Mutex<HashSet<i32>>> = 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<Vec<i32>, Error> {
|
||||||
|
let guard = IGNORESHOWS.lock().map_err(|err| format_err!("{}", err))?;
|
||||||
|
let keys = guard.iter().cloned().collect::<Vec<_>>();
|
||||||
|
Ok(keys)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn cleanup(cleanup_date: DateTime<Utc>) {
|
pub fn cleanup(cleanup_date: DateTime<Utc>) {
|
||||||
if let Err(err) = checkup(cleanup_date) {
|
if let Err(err) = checkup(cleanup_date) {
|
||||||
error!("Check up failed: {}", err);
|
error!("Check up failed: {}", err);
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use hammond_data::EpisodeWidgetQuery;
|
|||||||
use hammond_data::dbqueries;
|
use hammond_data::dbqueries;
|
||||||
|
|
||||||
use app::Action;
|
use app::Action;
|
||||||
use utils::get_pixbuf_from_path;
|
use utils::{get_ignored_shows, get_pixbuf_from_path};
|
||||||
use widgets::EpisodeWidget;
|
use widgets::EpisodeWidget;
|
||||||
|
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
@ -77,7 +77,8 @@ impl Default for EpisodesView {
|
|||||||
impl EpisodesView {
|
impl EpisodesView {
|
||||||
pub fn new(sender: Sender<Action>) -> Result<EpisodesView, Error> {
|
pub fn new(sender: Sender<Action>) -> Result<EpisodesView, Error> {
|
||||||
let view = EpisodesView::default();
|
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();
|
let now_utc = Utc::now();
|
||||||
|
|
||||||
episodes.into_iter().for_each(|ep| {
|
episodes.into_iter().for_each(|ep| {
|
||||||
|
|||||||
@ -6,7 +6,7 @@ use hammond_data::Podcast;
|
|||||||
use hammond_data::dbqueries;
|
use hammond_data::dbqueries;
|
||||||
|
|
||||||
use app::Action;
|
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::Arc;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
@ -54,7 +54,8 @@ impl ShowsPopulated {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn populate_flowbox(&self) -> Result<(), Error> {
|
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| {
|
podcasts.into_iter().map(Arc::new).for_each(|parent| {
|
||||||
let flowbox_child = ShowsChild::new(parent);
|
let flowbox_child = ShowsChild::new(parent);
|
||||||
|
|||||||
@ -7,7 +7,7 @@ use open;
|
|||||||
|
|
||||||
use hammond_data::Podcast;
|
use hammond_data::Podcast;
|
||||||
use hammond_data::dbqueries;
|
use hammond_data::dbqueries;
|
||||||
use hammond_data::utils::{delete_show, replace_extra_spaces};
|
use hammond_data::utils::replace_extra_spaces;
|
||||||
|
|
||||||
use app::Action;
|
use app::Action;
|
||||||
use utils::get_pixbuf_from_path;
|
use utils::get_pixbuf_from_path;
|
||||||
@ -15,7 +15,6 @@ use widgets::episode::episodes_listbox;
|
|||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::sync::mpsc::Sender;
|
use std::sync::mpsc::Sender;
|
||||||
use std::thread;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct ShowWidget {
|
pub struct ShowWidget {
|
||||||
@ -141,13 +140,7 @@ fn on_unsub_button_clicked(
|
|||||||
// hack to get away without properly checking for none.
|
// hack to get away without properly checking for none.
|
||||||
// if pressed twice would panic.
|
// if pressed twice would panic.
|
||||||
unsub_button.hide();
|
unsub_button.hide();
|
||||||
// Spawn a thread so it won't block the ui.
|
sender.send(Action::RemoveShow(pd))?;
|
||||||
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::HeaderBarNormal)?;
|
sender.send(Action::HeaderBarNormal)?;
|
||||||
sender.send(Action::ShowShowsAnimated)?;
|
sender.send(Action::ShowShowsAnimated)?;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user