From 10db4f721098573a3c285c9a58e80c8fae4a7b3a Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Mon, 5 Mar 2018 20:14:06 +0200 Subject: [PATCH 1/6] ShowWidget: Initial implementation of a menu popup. Re implement mark_all_episodes_as_watched functionality too. --- hammond-gtk/resources/gtk/show_widget.ui | 25 ++++++++++++++++++++++++ hammond-gtk/src/widgets/show.rs | 13 +++++++++++- 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/hammond-gtk/resources/gtk/show_widget.ui b/hammond-gtk/resources/gtk/show_widget.ui index fca068c..4e71bf5 100644 --- a/hammond-gtk/resources/gtk/show_widget.ui +++ b/hammond-gtk/resources/gtk/show_widget.ui @@ -266,4 +266,29 @@ Tobias Bernard + + False + bottom + + + True + False + vertical + + + True + True + True + Mark all epiodes as Watched + True + + + False + True + 0 + + + + + diff --git a/hammond-gtk/src/widgets/show.rs b/hammond-gtk/src/widgets/show.rs index 0a5808d..2805907 100644 --- a/hammond-gtk/src/widgets/show.rs +++ b/hammond-gtk/src/widgets/show.rs @@ -62,6 +62,8 @@ impl ShowWidget { } pub fn init(&self, pd: Arc, sender: Sender) { + let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/show_widget.ui"); + // Hacky workaround so the pd.id() can be retrieved from the `ShowStack`. WidgetExt::set_name(&self.container, &pd.id().to_string()); @@ -88,6 +90,16 @@ impl ShowWidget { error!("Error: {}", err); } }); + + let show_menu: gtk::Popover = builder.get_object("show_menu").unwrap(); + let mark_all: gtk::ModelButton = builder.get_object("mark_all_watched").unwrap(); + + mark_all.connect_clicked(clone!(pd, sender => move |_| { + if let Err(err) = on_played_button_clicked(&pd, sender.clone()) { + error!("Failed to mark all episodes as watched: {}", err); + } + })); + self.settings.set_popover(&show_menu); } /// Populate the listbox with the shows episodes. @@ -142,7 +154,6 @@ fn on_unsub_button_clicked( Ok(()) } -#[allow(dead_code)] fn on_played_button_clicked(pd: &Podcast, sender: Sender) -> Result<(), Error> { dbqueries::update_none_to_played_now(pd)?; sender.send(Action::RefreshWidget)?; From 9a73520b257c60fc2cbabbf27fa748c7c61c1ab5 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Mon, 5 Mar 2018 21:40:11 +0200 Subject: [PATCH 2/6] dbquerries: Add a unit test for update_none_to_played_now func. --- hammond-data/src/dbqueries.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/hammond-data/src/dbqueries.rs b/hammond-data/src/dbqueries.rs index 27c46f7..04bdcdc 100644 --- a/hammond-data/src/dbqueries.rs +++ b/hammond-data/src/dbqueries.rs @@ -348,3 +348,29 @@ pub fn update_none_to_played_now(parent: &Podcast) -> Result { .map_err(From::from) }) } + +#[cfg(test)] +mod tests { + use super::*; + use database::*; + use pipeline::*; + + #[test] + fn test_update_none_to_played_now() { + truncate_db().unwrap(); + + let url = "https://web.archive.org/web/20180120083840if_/https://feeds.feedburner.\ + com/InterceptedWithJeremyScahill"; + let source = Source::from_url(url).unwrap(); + let id = source.id(); + index_single_source(source, true).unwrap(); + let pd = get_podcast_from_source_id(id).unwrap(); + + let eps_num = get_pd_unplayed_episodes(&pd).unwrap().len(); + assert_ne!(eps_num, 0); + + update_none_to_played_now(&pd).unwrap(); + let eps_num2 = get_pd_unplayed_episodes(&pd).unwrap().len(); + assert_eq!(eps_num2, 0); + } +} From 8261b32c996b2507e35c227647fa768a353b83d3 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Mon, 5 Mar 2018 22:07:18 +0200 Subject: [PATCH 3/6] Update changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index cbf5e9b..c3caaa5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +* Ability to mark all episodes of a Show as watched. (#47)[https://gitlab.gnome.org/alatiera/Hammond/issues/47] + ## [0.3.0] - 2018-02-11 * Tobias Bernard Redesigned the whole Gtk+ client. From e4814dbfbc15ba07c947f2ca3887b58a9da4cf92 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Wed, 7 Mar 2018 16:37:10 +0200 Subject: [PATCH 4/6] ShowWidget: Initial prototype of an undo notification. --- hammond-gtk/resources/gtk/show_widget.ui | 289 +++++++++++++++-------- hammond-gtk/src/widgets/show.rs | 53 ++++- 2 files changed, 235 insertions(+), 107 deletions(-) diff --git a/hammond-gtk/resources/gtk/show_widget.ui b/hammond-gtk/resources/gtk/show_widget.ui index 4e71bf5..5bd1c86 100644 --- a/hammond-gtk/resources/gtk/show_widget.ui +++ b/hammond-gtk/resources/gtk/show_widget.ui @@ -68,167 +68,250 @@ Tobias Bernard - - 600 + True False - 32 - 32 - 32 - 32 - False - vertical - 24 + 600 True False - center - 12 - - - True - False - 128 - image-x-generic-symbolic - - - False - False - 0 - - + 32 + 32 + 32 + 32 + False + vertical + 24 True False - end - True - vertical - 6 - - + center + 12 + + True False - start - end - Show description - True - word-char - 100 - - - + 128 + image-x-generic-symbolic False False - 1 + 0 True False + end + True + vertical 6 - - + + True - True - True - - - True - False - center - center - emblem-system-symbolic - - + False + start + end + Show description + True + word-char + 100 + + + False - True - 0 - - - - - Website - True - True - True - center - center - - - False - True - 5 + False 1 - - Unsubscribe + True - True - True - center - center - + False + 6 + + + True + True + True + + + True + False + center + center + emblem-system-symbolic + + + + + False + True + 0 + + + + + Website + True + True + True + center + center + + + False + True + 5 + 1 + + + + + Unsubscribe + True + True + True + center + center + + + + False + True + 5 + end + 2 + + False - True - 5 + False end - 2 + 0 False - False - end - 0 + True + 1 False - True + False 1 + + + True + False + True + 0 + in + + + + + + + + + False + False + 2 + + - False - False - 0 + -1 - - + + True False - True - 0 - in + center + start - - - - + + True + False + + + True + False + center + center + 0 + none + + + True + False + center + center + 6 + 6 + 12 + + + True + False + center + center + An in-app action notification + + + False + False + 0 + + + + + Undo + True + True + True + center + center + + + False + False + end + 1 + + + + + + + + + + + + + -1 + - - False - False - 1 - diff --git a/hammond-gtk/src/widgets/show.rs b/hammond-gtk/src/widgets/show.rs index 2805907..bdd9ee0 100644 --- a/hammond-gtk/src/widgets/show.rs +++ b/hammond-gtk/src/widgets/show.rs @@ -1,5 +1,6 @@ use dissolve; use failure::Error; +use glib; use gtk; use gtk::prelude::*; use open; @@ -12,6 +13,8 @@ use app::Action; use utils::get_pixbuf_from_path; use widgets::episode::episodes_listbox; +use std::cell::RefCell; +use std::rc::Rc; use std::sync::Arc; use std::sync::mpsc::Sender; use std::thread; @@ -26,6 +29,9 @@ pub struct ShowWidget { settings: gtk::MenuButton, unsub: gtk::Button, episodes: gtk::Frame, + notif: gtk::Revealer, + notif_label: gtk::Label, + notif_undo: gtk::Button, } impl Default for ShowWidget { @@ -41,6 +47,10 @@ impl Default for ShowWidget { let link: gtk::Button = builder.get_object("link_button").unwrap(); let settings: gtk::MenuButton = builder.get_object("settings_button").unwrap(); + let notif: gtk::Revealer = builder.get_object("notif_revealer").unwrap(); + let notif_label: gtk::Label = builder.get_object("notif_label").unwrap(); + let notif_undo: gtk::Button = builder.get_object("undo_button").unwrap(); + ShowWidget { container, scrolled_window, @@ -50,6 +60,9 @@ impl Default for ShowWidget { link, settings, episodes, + notif, + notif_label, + notif_undo, } } } @@ -94,10 +107,11 @@ impl ShowWidget { let show_menu: gtk::Popover = builder.get_object("show_menu").unwrap(); let mark_all: gtk::ModelButton = builder.get_object("mark_all_watched").unwrap(); + let notif = self.notif.clone(); + let notif_label = self.notif_label.clone(); + let notif_undo = self.notif_undo.clone(); mark_all.connect_clicked(clone!(pd, sender => move |_| { - if let Err(err) = on_played_button_clicked(&pd, sender.clone()) { - error!("Failed to mark all episodes as watched: {}", err); - } + on_played_button_clicked(pd.clone(), ¬if, ¬if_label, ¬if_undo, sender.clone()) })); self.settings.set_popover(&show_menu); } @@ -154,7 +168,38 @@ fn on_unsub_button_clicked( Ok(()) } -fn on_played_button_clicked(pd: &Podcast, sender: Sender) -> Result<(), Error> { +fn on_played_button_clicked( + pd: Arc, + notif: >k::Revealer, + label: >k::Label, + undo: >k::Button, + sender: Sender, +) { + label.set_text("All episodes where marked as watched."); + notif.set_reveal_child(true); + + let id = timeout_add_seconds(10, move || { + if let Err(err) = wrap(&pd, sender.clone()) { + error!( + "Something went horribly wrong with the notif callback: {}", + err + ); + } + glib::Continue(false) + }); + + let id = Rc::new(RefCell::new(Some(id))); + + undo.connect_clicked(clone!(id, notif => move |_| { + let foo = id.borrow_mut().take(); + if let Some(id) = foo { + glib::source::source_remove(id); + notif.set_reveal_child(false); + } + })); +} + +fn wrap(pd: &Podcast, sender: Sender) -> Result<(), Error> { dbqueries::update_none_to_played_now(pd)?; sender.send(Action::RefreshWidget)?; Ok(()) From f6890c709f8f9db3db2dc36ae1a71646f2cecd23 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Wed, 7 Mar 2018 23:04:02 +0200 Subject: [PATCH 5/6] ShowWidget: Instantly dim episode titles when mark_all is clicked. This is would have been way prettier, easier and safer if we could have custom widgets. But till then I am not sure how to do it better. --- hammond-gtk/src/widgets/show.rs | 46 +++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/hammond-gtk/src/widgets/show.rs b/hammond-gtk/src/widgets/show.rs index bdd9ee0..6806d8d 100644 --- a/hammond-gtk/src/widgets/show.rs +++ b/hammond-gtk/src/widgets/show.rs @@ -110,8 +110,16 @@ impl ShowWidget { let notif = self.notif.clone(); let notif_label = self.notif_label.clone(); let notif_undo = self.notif_undo.clone(); + let episodes = self.episodes.clone(); mark_all.connect_clicked(clone!(pd, sender => move |_| { - on_played_button_clicked(pd.clone(), ¬if, ¬if_label, ¬if_undo, sender.clone()) + on_played_button_clicked( + pd.clone(), + ¬if, + ¬if_label, + ¬if_undo, + &episodes, + sender.clone() + ) })); self.settings.set_popover(&show_menu); } @@ -173,11 +181,18 @@ fn on_played_button_clicked( notif: >k::Revealer, label: >k::Label, undo: >k::Button, + episodes: >k::Frame, sender: Sender, ) { + if dim_titles(episodes).is_none() { + error!("Something went horribly wrong when dimming the titles."); + warn!("RUN WHILE YOU STILL CAN!"); + } + label.set_text("All episodes where marked as watched."); notif.set_reveal_child(true); + // Set up the callback let id = timeout_add_seconds(10, move || { if let Err(err) = wrap(&pd, sender.clone()) { error!( @@ -190,6 +205,7 @@ fn on_played_button_clicked( let id = Rc::new(RefCell::new(Some(id))); + // Cancel the callback undo.connect_clicked(clone!(id, notif => move |_| { let foo = id.borrow_mut().take(); if let Some(id) = foo { @@ -201,6 +217,32 @@ fn on_played_button_clicked( fn wrap(pd: &Podcast, sender: Sender) -> Result<(), Error> { dbqueries::update_none_to_played_now(pd)?; - sender.send(Action::RefreshWidget)?; + sender.send(Action::RefreshWidgetIfVis)?; + sender.send(Action::RefreshEpisodesView)?; Ok(()) } + +// Ideally if we had a custom widget this would have been as simple as: +// `for row in listbox { ep = row.get_episode(); ep.dim_title(); }` +// But now I can't think of a better way to do it than hardcoding the title +// position relative to the EpisodeWidget container gtk::Box. +fn dim_titles(episodes: >k::Frame) -> Option<()> { + let listbox = episodes.get_focus_child()?.downcast::().ok()?; + let children = listbox.get_children(); + + for row in children { + let row = row.downcast::().ok()?; + let container = row.get_children().remove(0).downcast::().ok()?; + let foo = container + .get_children() + .remove(0) + .downcast::() + .ok()?; + let bar = foo.get_children().remove(0).downcast::().ok()?; + let baz = bar.get_children().remove(0).downcast::().ok()?; + let title = baz.get_children().remove(0).downcast::().ok()?; + + title.get_style_context().map(|c| c.add_class("dim-label")); + } + Some(()) +} From 7b064e63b9d38ad62129447517150e8fa84dfe4a Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Thu, 8 Mar 2018 16:21:42 +0200 Subject: [PATCH 6/6] ShowWidget: Fix undo notif. --- hammond-gtk/src/widgets/show.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/hammond-gtk/src/widgets/show.rs b/hammond-gtk/src/widgets/show.rs index 6806d8d..b2791f5 100644 --- a/hammond-gtk/src/widgets/show.rs +++ b/hammond-gtk/src/widgets/show.rs @@ -193,7 +193,9 @@ fn on_played_button_clicked( notif.set_reveal_child(true); // Set up the callback - let id = timeout_add_seconds(10, move || { + let id = timeout_add_seconds( + 10, + clone!(sender => move || { if let Err(err) = wrap(&pd, sender.clone()) { error!( "Something went horribly wrong with the notif callback: {}", @@ -201,16 +203,20 @@ fn on_played_button_clicked( ); } glib::Continue(false) - }); + }), + ); let id = Rc::new(RefCell::new(Some(id))); // Cancel the callback - undo.connect_clicked(clone!(id, notif => move |_| { + undo.connect_clicked(clone!(id, notif, sender => move |_| { let foo = id.borrow_mut().take(); if let Some(id) = foo { glib::source::source_remove(id); notif.set_reveal_child(false); + if let Err(err) = sender.send(Action::RefreshWidgetIfVis) { + error!("Something went horribly wrong with the Action channel: {}", err) + } } })); } @@ -227,7 +233,11 @@ fn wrap(pd: &Podcast, sender: Sender) -> Result<(), Error> { // But now I can't think of a better way to do it than hardcoding the title // position relative to the EpisodeWidget container gtk::Box. fn dim_titles(episodes: >k::Frame) -> Option<()> { - let listbox = episodes.get_focus_child()?.downcast::().ok()?; + let listbox = episodes + .get_children() + .remove(0) + .downcast::() + .ok()?; let children = listbox.get_children(); for row in children {