From 3da503cdeaed690c787a2e6a6dd78f3d5455cb9b Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Mon, 26 Feb 2018 17:49:20 +0200 Subject: [PATCH 01/23] Use prebuilt image for the rustfmt CI check. --- .gitlab-ci.yml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0f9f325..6d0f13a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -39,13 +39,10 @@ stable:test: # Configure and run rustfmt on nightly # Exits and builds fails if on bad format rustfmt: - image: "rustlang/rust:nightly" + image: "registry.gitlab.com/alatiera/rustfmt-oci-image/rustfmt:nightly" stage: lint - variables: - CFG_RELEASE_CHANNEL: "nightly" script: - rustc --version && cargo --version - - cargo install rustfmt-nightly --force - cargo fmt --all -- --write-mode=diff # Configure and run clippy on nightly From bb467b7abadeba2ee8ee6362467318b9578ce9c9 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Sat, 3 Mar 2018 16:45:37 +0200 Subject: [PATCH 02/23] Rustfmt. --- hammond-data/src/database.rs | 7 +--- hammond-data/src/models/new_episode.rs | 49 ++++++++++------------ hammond-data/src/models/new_podcast.rs | 57 ++++++++++++-------------- hammond-gtk/src/manager.rs | 5 +-- hammond-gtk/src/utils.rs | 5 +-- 5 files changed, 53 insertions(+), 70 deletions(-) diff --git a/hammond-data/src/database.rs b/hammond-data/src/database.rs index d6e5be0..8db7e6a 100644 --- a/hammond-data/src/database.rs +++ b/hammond-data/src/database.rs @@ -16,7 +16,7 @@ type Pool = r2d2::Pool>; embed_migrations!("migrations/"); -lazy_static!{ +lazy_static! { static ref POOL: Pool = init_pool(DB_PATH.to_str().unwrap()); } @@ -30,10 +30,7 @@ extern crate tempdir; #[cfg(test)] lazy_static! { - static ref TEMPDIR: tempdir::TempDir = { - tempdir::TempDir::new("hammond_unit_test").unwrap() - }; - + static ref TEMPDIR: tempdir::TempDir = { tempdir::TempDir::new("hammond_unit_test").unwrap() }; static ref DB_PATH: PathBuf = TEMPDIR.path().join("hammond.db"); } diff --git a/hammond-data/src/models/new_episode.rs b/hammond-data/src/models/new_episode.rs index b0ce47c..ad1a12d 100644 --- a/hammond-data/src/models/new_episode.rs +++ b/hammond-data/src/models/new_episode.rs @@ -311,7 +311,6 @@ mod tests { .build() .unwrap() }; - static ref EXPECTED_MINIMAL_INTERCEPTED_2: NewEpisodeMinimal = { NewEpisodeMinimalBuilder::default() .title("Atlas Golfed — U.S.-Backed Think Tanks Target Latin America") @@ -325,13 +324,12 @@ mod tests { .build() .unwrap() }; - static ref EXPECTED_INTERCEPTED_1: NewEpisode = { - let descr = "NSA whistleblower Edward Snowden discusses the massive Equifax data breach \ - and allegations of Russian interference in the US election. Commentator \ - Shaun King explains his call for a boycott of the NFL and talks about his \ - campaign to bring violent neo-Nazis to justice. Rapper Open Mike Eagle \ - performs."; + let descr = "NSA whistleblower Edward Snowden discusses the massive Equifax data \ + breach and allegations of Russian interference in the US election. \ + Commentator Shaun King explains his call for a boycott of the NFL and \ + talks about his campaign to bring violent neo-Nazis to justice. Rapper \ + Open Mike Eagle performs."; NewEpisodeBuilder::default() .title("The Super Bowl of Racism") @@ -347,16 +345,15 @@ mod tests { .build() .unwrap() }; - static ref EXPECTED_INTERCEPTED_2: NewEpisode = { let descr = "This week on Intercepted: Jeremy gives an update on the aftermath of \ - Blackwater’s 2007 massacre of Iraqi civilians. Intercept reporter Lee Fang \ - lays out how a network of libertarian think tanks called the Atlas Network \ - is insidiously shaping political infrastructure in Latin America. We speak \ - with attorney and former Hugo Chavez adviser Eva Golinger about the \ - Venezuela\'s political turmoil.And we hear Claudia Lizardo of the \ - Caracas-based band, La Pequeña Revancha, talk about her music and hopes for \ - Venezuela."; + Blackwater’s 2007 massacre of Iraqi civilians. Intercept reporter Lee \ + Fang lays out how a network of libertarian think tanks called the Atlas \ + Network is insidiously shaping political infrastructure in Latin \ + America. We speak with attorney and former Hugo Chavez adviser Eva \ + Golinger about the Venezuela\'s political turmoil.And we hear Claudia \ + Lizardo of the Caracas-based band, La Pequeña Revancha, talk about her \ + music and hopes for Venezuela."; NewEpisodeBuilder::default() .title("Atlas Golfed — U.S.-Backed Think Tanks Target Latin America") @@ -372,7 +369,6 @@ mod tests { .build() .unwrap() }; - static ref UPDATED_DURATION_INTERCEPTED_1: NewEpisode = { NewEpisodeBuilder::default() .title("The Super Bowl of Racism") @@ -388,7 +384,6 @@ mod tests { .build() .unwrap() }; - static ref EXPECTED_MINIMAL_LUP_1: NewEpisodeMinimal = { NewEpisodeMinimalBuilder::default() .title("Hacking Devices with Kali Linux | LUP 214") @@ -402,7 +397,6 @@ mod tests { .build() .unwrap() }; - static ref EXPECTED_MINIMAL_LUP_2: NewEpisodeMinimal = { NewEpisodeMinimalBuilder::default() .title("Gnome Does it Again | LUP 213") @@ -416,12 +410,11 @@ mod tests { .build() .unwrap() }; - static ref EXPECTED_LUP_1: NewEpisode = { let descr = "Audit your network with a couple of easy commands on Kali Linux. Chris \ - decides to blow off a little steam by attacking his IoT devices, Wes has the \ - scope on Equifax blaming open source & the Beard just saved the show. \ - It’s a really packed episode!"; + decides to blow off a little steam by attacking his IoT devices, Wes has \ + the scope on Equifax blaming open source & the Beard just saved the \ + show. It’s a really packed episode!"; NewEpisodeBuilder::default() .title("Hacking Devices with Kali Linux | LUP 214") @@ -437,13 +430,13 @@ mod tests { .build() .unwrap() }; - static ref EXPECTED_LUP_2: NewEpisode = { - let descr = "The Gnome project is about to solve one of our audience's biggest Wayland’s \ - concerns. But as the project takes on a new level of relevance, decisions for the \ - next version of Gnome have us worried about the future.\nPlus we chat with Wimpy \ - about the Ubuntu Rally in NYC, Microsoft’s sneaky move to turn Windows 10 into the \ - “ULTIMATE LINUX RUNTIME”, community news & more!"; + let descr = "The Gnome project is about to solve one of our audience's biggest \ + Wayland’s concerns. But as the project takes on a new level of \ + relevance, decisions for the next version of Gnome have us worried about \ + the future.\nPlus we chat with Wimpy about the Ubuntu Rally in NYC, \ + Microsoft’s sneaky move to turn Windows 10 into the “ULTIMATE LINUX \ + RUNTIME”, community news & more!"; NewEpisodeBuilder::default() .title("Gnome Does it Again | LUP 213") diff --git a/hammond-data/src/models/new_podcast.rs b/hammond-data/src/models/new_podcast.rs index ce2d1c6..22f8d51 100644 --- a/hammond-data/src/models/new_podcast.rs +++ b/hammond-data/src/models/new_podcast.rs @@ -162,13 +162,14 @@ mod tests { use std::io::BufReader; // Pre-built expected NewPodcast structs. - lazy_static!{ + lazy_static! { static ref EXPECTED_INTERCEPTED: NewPodcast = { let descr = "The people behind The Intercept’s fearless reporting and incisive \ - commentary—Jeremy Scahill, Glenn Greenwald, Betsy Reed and others—discuss \ - the crucial issues of our time: national security, civil liberties, foreign \ - policy, and criminal justice. Plus interviews with artists, thinkers, and \ - newsmakers who challenge our preconceptions about the world we live in."; + commentary—Jeremy Scahill, Glenn Greenwald, Betsy Reed and \ + others—discuss the crucial issues of our time: national security, civil \ + liberties, foreign policy, and criminal justice. Plus interviews with \ + artists, thinkers, and newsmakers who challenge our preconceptions about \ + the world we live in."; NewPodcastBuilder::default() .title("Intercepted with Jeremy Scahill") @@ -183,11 +184,10 @@ mod tests { .build() .unwrap() }; - static ref EXPECTED_LUP: NewPodcast = { - let descr = "An open show powered by community LINUX Unplugged takes the best attributes \ - of open collaboration and focuses them into a weekly lifestyle show about \ - Linux."; + let descr = "An open show powered by community LINUX Unplugged takes the best \ + attributes of open collaboration and focuses them into a weekly \ + lifestyle show about Linux."; NewPodcastBuilder::default() .title("LINUX Unplugged Podcast") @@ -200,17 +200,16 @@ mod tests { .build() .unwrap() }; - static ref EXPECTED_TIPOFF: NewPodcast = { - let desc = "Welcome to The Tip Off- the podcast where we take you behind the scenes of \ - some of the best investigative journalism from recent years. Each episode \ - we’ll be digging into an investigative scoop- hearing from the journalists \ - behind the work as they tell us about the leads, the dead-ends and of course, \ - the tip offs. There’ll be car chases, slammed doors, terrorist cells, \ - meetings in dimly lit bars and cafes, wrangling with despotic regimes and \ - much more. So if you’re curious about the fun, complicated detective work \ - that goes into doing great investigative journalism- then this is the podcast \ - for you."; + let desc = "Welcome to The Tip Off- the podcast where we take you behind the scenes \ + of some of the best investigative journalism from recent years. Each \ + episode we’ll be digging into an investigative scoop- hearing from the \ + journalists behind the work as they tell us about the leads, the \ + dead-ends and of course, the tip offs. There’ll be car chases, slammed \ + doors, terrorist cells, meetings in dimly lit bars and cafes, wrangling \ + with despotic regimes and much more. So if you’re curious about the fun, \ + complicated detective work that goes into doing great investigative \ + journalism- then this is the podcast for you."; NewPodcastBuilder::default() .title("The Tip Off") @@ -223,15 +222,13 @@ mod tests { .source_id(42) .build() .unwrap() - }; - static ref EXPECTED_STARS: NewPodcast = { - let descr = "

The first audio drama from Tor Labs and Gideon Media, Steal the Stars is \ - a gripping noir science fiction thriller in 14 episodes: Forbidden love, a \ - crashed UFO, an alien body, and an impossible heist unlike any ever \ - attempted - scripted by Mac Rogers, the award-winning playwright and writer \ - of the multi-million download The Message and LifeAfter.

"; + let descr = "

The first audio drama from Tor Labs and Gideon Media, Steal the Stars \ + is a gripping noir science fiction thriller in 14 episodes: Forbidden \ + love, a crashed UFO, an alien body, and an impossible heist unlike any \ + ever attempted - scripted by Mac Rogers, the award-winning playwright \ + and writer of the multi-million download The Message and LifeAfter.

"; let img = "https://dfkfj8j276wwv.cloudfront.net/images/2c/5f/a0/1a/2c5fa01a-ae78-4a8c-\ b183-7311d2e436c3/b3a4aa57a576bb662191f2a6bc2a436c8c4ae256ecffaff5c4c54fd42e\ 923914941c264d01efb1833234b52c9530e67d28a8cebbe3d11a4bc0fbbdf13ecdf1c3.jpeg"; @@ -245,11 +242,10 @@ mod tests { .build() .unwrap() }; - static ref EXPECTED_CODE: NewPodcast = { - let descr = "A podcast about humans and technology. Panelists: Coraline Ada Ehmke, David \ - Brady, Jessica Kerr, Jay Bobo, Astrid Countee and Sam Livingston-Gray. \ - Brought to you by @therubyrep."; + let descr = "A podcast about humans and technology. Panelists: Coraline Ada Ehmke, \ + David Brady, Jessica Kerr, Jay Bobo, Astrid Countee and Sam \ + Livingston-Gray. Brought to you by @therubyrep."; NewPodcastBuilder::default() .title("Greater Than Code") @@ -262,7 +258,6 @@ mod tests { .build() .unwrap() }; - static ref UPDATED_DESC_INTERCEPTED: NewPodcast = { NewPodcastBuilder::default() .title("Intercepted with Jeremy Scahill") diff --git a/hammond-gtk/src/manager.rs b/hammond-gtk/src/manager.rs index a9d70fe..1c86a1e 100644 --- a/hammond-gtk/src/manager.rs +++ b/hammond-gtk/src/manager.rs @@ -73,9 +73,8 @@ impl DownloadProgress for Progress { } lazy_static! { - pub static ref ACTIVE_DOWNLOADS: Arc>>>> = { - Arc::new(RwLock::new(HashMap::new())) - }; + pub static ref ACTIVE_DOWNLOADS: Arc>>>> = + { Arc::new(RwLock::new(HashMap::new())) }; } pub fn add(id: i32, directory: &str, sender: Sender) -> Result<(), Error> { diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index f2b0876..26ee2be 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -74,9 +74,8 @@ fn refresh_feed(source: Option>, sender: Sender) -> Result<( } lazy_static! { - static ref CACHED_PIXBUFS: RwLock>>> = { - RwLock::new(HashMap::new()) - }; + static ref CACHED_PIXBUFS: RwLock>>> = + { RwLock::new(HashMap::new()) }; } // Since gdk_pixbuf::Pixbuf is refference counted and every episode, From 10db4f721098573a3c285c9a58e80c8fae4a7b3a Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Mon, 5 Mar 2018 20:14:06 +0200 Subject: [PATCH 03/23] 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 04/23] 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 05/23] 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 06/23] 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 07/23] 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 08/23] 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 { From 7de118adeb542d37de0cada59c1cbbda1b6f8fc7 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Thu, 8 Mar 2018 23:14:48 +0200 Subject: [PATCH 09/23] Minor style changes. --- hammond-gtk/src/app.rs | 5 ++--- hammond-gtk/src/widgets/show.rs | 25 +++++++++++-------------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/hammond-gtk/src/app.rs b/hammond-gtk/src/app.rs index 8027d4b..7f47456 100644 --- a/hammond-gtk/src/app.rs +++ b/hammond-gtk/src/app.rs @@ -114,9 +114,8 @@ impl App { pub fn run(self) { let window = self.window.clone(); - let app = self.app_instance.clone(); - self.app_instance.connect_startup(move |_| { - build_ui(&window, &app); + self.app_instance.connect_startup(move |app| { + build_ui(&window, app); }); self.setup_timed_callbacks(); diff --git a/hammond-gtk/src/widgets/show.rs b/hammond-gtk/src/widgets/show.rs index b2791f5..1d7597c 100644 --- a/hammond-gtk/src/widgets/show.rs +++ b/hammond-gtk/src/widgets/show.rs @@ -193,19 +193,13 @@ fn on_played_button_clicked( notif.set_reveal_child(true); // Set up the callback - let id = timeout_add_seconds( - 10, - clone!(sender => move || { + let callback = clone!(sender => move || { if let Err(err) = wrap(&pd, sender.clone()) { - error!( - "Something went horribly wrong with the notif callback: {}", - err - ); + error!("Something went horribly wrong with the notif callback: {}", err); } glib::Continue(false) - }), - ); - + }); + let id = timeout_add_seconds(10, callback); let id = Rc::new(RefCell::new(Some(id))); // Cancel the callback @@ -213,10 +207,13 @@ fn on_played_button_clicked( 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) - } + } + + // Hide the notification + notif.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) } })); } From d1821163c2afca3679be8e6ebaf630541c34c716 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 9 Mar 2018 01:21:54 +0200 Subject: [PATCH 10/23] Factor out the In-app notification into something generic. --- hammond-gtk/resources/gtk/inapp_notif.ui | 77 ++++++ hammond-gtk/resources/gtk/show_widget.ui | 289 ++++++++--------------- hammond-gtk/src/app.rs | 10 +- hammond-gtk/src/appnotif.rs | 68 ++++++ hammond-gtk/src/main.rs | 1 + hammond-gtk/src/widgets/show.rs | 49 +--- 6 files changed, 261 insertions(+), 233 deletions(-) create mode 100644 hammond-gtk/resources/gtk/inapp_notif.ui create mode 100644 hammond-gtk/src/appnotif.rs diff --git a/hammond-gtk/resources/gtk/inapp_notif.ui b/hammond-gtk/resources/gtk/inapp_notif.ui new file mode 100644 index 0000000..c14a5e9 --- /dev/null +++ b/hammond-gtk/resources/gtk/inapp_notif.ui @@ -0,0 +1,77 @@ + + + + + + True + False + 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 + + + + diff --git a/hammond-gtk/resources/gtk/show_widget.ui b/hammond-gtk/resources/gtk/show_widget.ui index 5bd1c86..2b54c11 100644 --- a/hammond-gtk/resources/gtk/show_widget.ui +++ b/hammond-gtk/resources/gtk/show_widget.ui @@ -68,250 +68,167 @@ Tobias Bernard - + + 600 True False + 32 + 32 + 32 + 32 + False + vertical + 24 - 600 True False - 32 - 32 - 32 - 32 - False - vertical - 24 + center + 12 + + + True + False + 128 + image-x-generic-symbolic + + + False + False + 0 + + True False - center - 12 - - + end + True + vertical + 6 + + True False - 128 - image-x-generic-symbolic + start + end + Show description + True + word-char + 100 + + + False False - 0 + 1 True False - end - True - vertical 6 - - + + True - False - start - end - Show description - True - word-char - 100 - - - + True + True + + + True + False + center + center + emblem-system-symbolic + + False - False + True + 0 + + + + + Website + True + True + True + center + center + + + False + True + 5 1 - + + Unsubscribe True - 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 - - + True + True + center + center + False - False + True + 5 end - 0 + 2 False - True - 1 + False + end + 0 False - False + True 1 - - - True - False - True - 0 - in - - - - - - - - - False - False - 2 - - - -1 + False + False + 1 - - + + True False - center - start + True + 0 + in - - 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 + 2 + diff --git a/hammond-gtk/src/app.rs b/hammond-gtk/src/app.rs index 7f47456..215ecf9 100644 --- a/hammond-gtk/src/app.rs +++ b/hammond-gtk/src/app.rs @@ -39,6 +39,7 @@ pub enum Action { pub struct App { app_instance: gtk::Application, window: gtk::Window, + overlay: gtk::Overlay, header: Arc
, content: Arc, receiver: Receiver, @@ -73,12 +74,17 @@ impl App { // Create the headerbar let header = Arc::new(Header::new(&content, &window, sender.clone())); - // Add the content main stack to the window. - window.add(&content.get_stack()); + // Add the content main stack to the overlay. + let overlay = gtk::Overlay::new(); + overlay.add(&content.get_stack()); + + // Add the overlay to the main window + window.add(&overlay); App { app_instance: application, window, + overlay, header, content, receiver, diff --git a/hammond-gtk/src/appnotif.rs b/hammond-gtk/src/appnotif.rs new file mode 100644 index 0000000..593688c --- /dev/null +++ b/hammond-gtk/src/appnotif.rs @@ -0,0 +1,68 @@ +use glib; +use gtk; +use gtk::prelude::*; + +use app::Action; + +use std::cell::RefCell; +use std::rc::Rc; +use std::sync::mpsc::Sender; + +#[derive(Debug, Clone)] +pub struct InAppNotification { + revealer: gtk::Revealer, + text: gtk::Label, + undo: gtk::Button, +} + +impl Default for InAppNotification { + fn default() -> Self { + let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/inappp_notif.ui"); + + let revealer: gtk::Revealer = builder.get_object("notif_revealer").unwrap(); + let text: gtk::Label = builder.get_object("notif_label").unwrap(); + let undo: gtk::Button = builder.get_object("undo_button").unwrap(); + + InAppNotification { + revealer, + text, + undo, + } + } +} + +impl InAppNotification { + pub fn new(text: &str, callback: F, sender: Sender) -> Self + where + F: FnMut() -> Continue + 'static, + { + let notif = InAppNotification::default(); + + notif.text.set_text(text); + notif.revealer.set_reveal_child(true); + + let id = timeout_add_seconds(10, callback); + let id = Rc::new(RefCell::new(Some(id))); + + // Cancel the callback + let revealer = notif.revealer.clone(); + notif.undo.connect_clicked(move |_| { + let foo = id.borrow_mut().take(); + if let Some(id) = foo { + glib::source::source_remove(id); + } + + // 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 + ) + } + }); + + notif + } +} diff --git a/hammond-gtk/src/main.rs b/hammond-gtk/src/main.rs index fa09b19..013fcdc 100644 --- a/hammond-gtk/src/main.rs +++ b/hammond-gtk/src/main.rs @@ -65,6 +65,7 @@ pub mod app; pub mod utils; pub mod manager; pub mod static_resource; +pub mod appnotif; use app::App; diff --git a/hammond-gtk/src/widgets/show.rs b/hammond-gtk/src/widgets/show.rs index 1d7597c..118d830 100644 --- a/hammond-gtk/src/widgets/show.rs +++ b/hammond-gtk/src/widgets/show.rs @@ -10,11 +10,10 @@ use hammond_data::dbqueries; use hammond_data::utils::{delete_show, replace_extra_spaces}; use app::Action; +use appnotif::InAppNotification; 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; @@ -29,9 +28,6 @@ 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 { @@ -47,10 +43,6 @@ 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, @@ -60,9 +52,6 @@ impl Default for ShowWidget { link, settings, episodes, - notif, - notif_label, - notif_undo, } } } @@ -107,16 +96,10 @@ 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(); let episodes = self.episodes.clone(); mark_all.connect_clicked(clone!(pd, sender => move |_| { on_played_button_clicked( pd.clone(), - ¬if, - ¬if_label, - ¬if_undo, &episodes, sender.clone() ) @@ -176,21 +159,13 @@ fn on_unsub_button_clicked( Ok(()) } -fn on_played_button_clicked( - pd: Arc, - notif: >k::Revealer, - label: >k::Label, - undo: >k::Button, - episodes: >k::Frame, - sender: Sender, -) { +fn on_played_button_clicked(pd: Arc, 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); + let text = "All episodes where marked as watched."; // Set up the callback let callback = clone!(sender => move || { @@ -199,23 +174,7 @@ fn on_played_button_clicked( } glib::Continue(false) }); - let id = timeout_add_seconds(10, callback); - let id = Rc::new(RefCell::new(Some(id))); - - // Cancel the callback - undo.connect_clicked(clone!(id, notif, sender => move |_| { - let foo = id.borrow_mut().take(); - if let Some(id) = foo { - glib::source::source_remove(id); - } - - // Hide the notification - notif.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) - } - })); + let _notif = InAppNotification::new(text, callback, sender); } fn wrap(pd: &Podcast, sender: Sender) -> Result<(), Error> { From 82988b60117027583010c8473ce3f31c2c19eacc Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 9 Mar 2018 14:53:13 +0200 Subject: [PATCH 11/23] Implement in-app notifications as App wide actions. At first I tried creating custom InAppNotifications and send them to the main loop to be added. That does not work sicne gtk widgets are not thread safe. For now we can try having Action messages that create them on demand. As oppose to create first then pass them. --- hammond-gtk/resources/gtk/inapp_notif.ui | 2 +- hammond-gtk/resources/resources.xml | 1 + hammond-gtk/src/app.rs | 21 ++++++++++++++++++++- hammond-gtk/src/appnotif.rs | 13 ++++++++----- hammond-gtk/src/widgets/mod.rs | 1 + hammond-gtk/src/widgets/show.rs | 16 +++------------- 6 files changed, 34 insertions(+), 20 deletions(-) diff --git a/hammond-gtk/resources/gtk/inapp_notif.ui b/hammond-gtk/resources/gtk/inapp_notif.ui index c14a5e9..443be4a 100644 --- a/hammond-gtk/resources/gtk/inapp_notif.ui +++ b/hammond-gtk/resources/gtk/inapp_notif.ui @@ -2,7 +2,7 @@ - + True False center diff --git a/hammond-gtk/resources/resources.xml b/hammond-gtk/resources/resources.xml index 366f71c..51b44cf 100644 --- a/hammond-gtk/resources/resources.xml +++ b/hammond-gtk/resources/resources.xml @@ -9,6 +9,7 @@ gtk/shows_view.ui gtk/shows_child.ui gtk/headerbar.ui + gtk/inapp_notif.ui gtk/style.css diff --git a/hammond-gtk/src/app.rs b/hammond-gtk/src/app.rs index 215ecf9..0f48e97 100644 --- a/hammond-gtk/src/app.rs +++ b/hammond-gtk/src/app.rs @@ -8,9 +8,11 @@ use gtk::prelude::*; use hammond_data::{Podcast, Source}; use hammond_data::utils::checkup; +use appnotif::*; use headerbar::Header; use stacks::Content; use utils; +use widgets::mark_all_watched; use std::sync::Arc; use std::sync::mpsc::{channel, Receiver, Sender}; @@ -33,6 +35,7 @@ pub enum Action { HeaderBarNormal, HeaderBarShowUpdateIndicator, HeaderBarHideUpdateIndicator, + MarkAllPlayerNotification(Arc), } #[derive(Debug)] @@ -92,7 +95,7 @@ impl App { } } - pub fn setup_timed_callbacks(&self) { + fn setup_timed_callbacks(&self) { let sender = self.sender.clone(); // Update the feeds right after the Application is initialized. gtk::timeout_add_seconds(2, move || { @@ -128,6 +131,7 @@ impl App { let content = self.content.clone(); let headerbar = self.header.clone(); let sender = self.sender.clone(); + let overlay = self.overlay.clone(); let receiver = self.receiver; gtk::idle_add(move || { match receiver.recv_timeout(Duration::from_millis(10)) { @@ -157,6 +161,21 @@ impl App { Ok(Action::HeaderBarNormal) => headerbar.switch_to_normal(), Ok(Action::HeaderBarShowUpdateIndicator) => headerbar.show_update_notification(), Ok(Action::HeaderBarHideUpdateIndicator) => headerbar.hide_update_notification(), + Ok(Action::MarkAllPlayerNotification(pd)) => { + let sender = sender.clone(); + let callback = clone!(sender => move || { + if let Err(err) = mark_all_watched(&pd, sender.clone()) { + error!("Something went horribly wrong with the notif callback: {}", err); + } + glib::Continue(false) + }); + let text = "All episodes where marked as watched."; + + let sender = sender.clone(); + let notif = InAppNotification::new(text.into(), callback, sender); + overlay.add_overlay(¬if.overlay); + overlay.show_all(); + } Err(_) => (), } diff --git a/hammond-gtk/src/appnotif.rs b/hammond-gtk/src/appnotif.rs index 593688c..879f2f2 100644 --- a/hammond-gtk/src/appnotif.rs +++ b/hammond-gtk/src/appnotif.rs @@ -10,6 +10,7 @@ use std::sync::mpsc::Sender; #[derive(Debug, Clone)] pub struct InAppNotification { + pub overlay: gtk::Overlay, revealer: gtk::Revealer, text: gtk::Label, undo: gtk::Button, @@ -17,13 +18,15 @@ pub struct InAppNotification { impl Default for InAppNotification { fn default() -> Self { - let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/inappp_notif.ui"); + let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/inapp_notif.ui"); + let overlay: gtk::Overlay = builder.get_object("notif_overlay").unwrap(); let revealer: gtk::Revealer = builder.get_object("notif_revealer").unwrap(); let text: gtk::Label = builder.get_object("notif_label").unwrap(); let undo: gtk::Button = builder.get_object("undo_button").unwrap(); InAppNotification { + overlay, revealer, text, undo, @@ -32,16 +35,16 @@ impl Default for InAppNotification { } impl InAppNotification { - pub fn new(text: &str, callback: F, sender: Sender) -> Self + pub fn new(text: String, callback: F, sender: Sender) -> Self where - F: FnMut() -> Continue + 'static, + F: FnMut() -> glib::Continue + 'static, { let notif = InAppNotification::default(); - notif.text.set_text(text); + notif.text.set_text(&text); notif.revealer.set_reveal_child(true); - let id = timeout_add_seconds(10, callback); + let id = timeout_add_seconds(60, callback); let id = Rc::new(RefCell::new(Some(id))); // Cancel the callback diff --git a/hammond-gtk/src/widgets/mod.rs b/hammond-gtk/src/widgets/mod.rs index 3b348fa..c6aa66c 100644 --- a/hammond-gtk/src/widgets/mod.rs +++ b/hammond-gtk/src/widgets/mod.rs @@ -3,3 +3,4 @@ mod episode; pub use self::episode::EpisodeWidget; pub use self::show::ShowWidget; +pub use self::show::mark_all_watched; diff --git a/hammond-gtk/src/widgets/show.rs b/hammond-gtk/src/widgets/show.rs index 118d830..abe9a37 100644 --- a/hammond-gtk/src/widgets/show.rs +++ b/hammond-gtk/src/widgets/show.rs @@ -1,6 +1,6 @@ use dissolve; use failure::Error; -use glib; +// use glib; use gtk; use gtk::prelude::*; use open; @@ -10,7 +10,6 @@ use hammond_data::dbqueries; use hammond_data::utils::{delete_show, replace_extra_spaces}; use app::Action; -use appnotif::InAppNotification; use utils::get_pixbuf_from_path; use widgets::episode::episodes_listbox; @@ -165,19 +164,10 @@ fn on_played_button_clicked(pd: Arc, episodes: >k::Frame, sender: Sen warn!("RUN WHILE YOU STILL CAN!"); } - let text = "All episodes where marked as watched."; - - // Set up the callback - let callback = clone!(sender => move || { - if let Err(err) = wrap(&pd, sender.clone()) { - error!("Something went horribly wrong with the notif callback: {}", err); - } - glib::Continue(false) - }); - let _notif = InAppNotification::new(text, callback, sender); + sender.send(Action::MarkAllPlayerNotification(pd)).unwrap(); } -fn wrap(pd: &Podcast, sender: Sender) -> Result<(), Error> { +pub fn mark_all_watched(pd: &Podcast, sender: Sender) -> Result<(), Error> { dbqueries::update_none_to_played_now(pd)?; sender.send(Action::RefreshWidgetIfVis)?; sender.send(Action::RefreshEpisodesView)?; From 483fd090f112e7f625247d30984cc8acb990376d Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 9 Mar 2018 15:25:53 +0200 Subject: [PATCH 12/23] InAppNotification: Add close button. --- hammond-gtk/resources/gtk/inapp_notif.ui | 34 ++++++++++++++++++++---- hammond-gtk/src/app.rs | 4 ++- hammond-gtk/src/appnotif.rs | 29 +++++++++++++++----- 3 files changed, 54 insertions(+), 13 deletions(-) diff --git a/hammond-gtk/resources/gtk/inapp_notif.ui b/hammond-gtk/resources/gtk/inapp_notif.ui index 443be4a..4074fd8 100644 --- a/hammond-gtk/resources/gtk/inapp_notif.ui +++ b/hammond-gtk/resources/gtk/inapp_notif.ui @@ -2,13 +2,13 @@ - + True False center start - + True False @@ -29,7 +29,7 @@ 6 12 - + True False center @@ -43,7 +43,31 @@ - + + True + True + False + True + center + center + none + + + True + False + window-close-symbolic + + + + + False + False + end + 1 + + + + Undo True True @@ -55,7 +79,7 @@ False False end - 1 + 2 diff --git a/hammond-gtk/src/app.rs b/hammond-gtk/src/app.rs index 0f48e97..9a4e8b1 100644 --- a/hammond-gtk/src/app.rs +++ b/hammond-gtk/src/app.rs @@ -174,7 +174,9 @@ impl App { let sender = sender.clone(); let notif = InAppNotification::new(text.into(), callback, sender); overlay.add_overlay(¬if.overlay); - overlay.show_all(); + // We need to display the notification after the widget is added to the overlay + // so there will be a nice animation. + notif.show(); } Err(_) => (), } diff --git a/hammond-gtk/src/appnotif.rs b/hammond-gtk/src/appnotif.rs index 879f2f2..a949fae 100644 --- a/hammond-gtk/src/appnotif.rs +++ b/hammond-gtk/src/appnotif.rs @@ -14,22 +14,25 @@ pub struct InAppNotification { revealer: gtk::Revealer, text: gtk::Label, undo: gtk::Button, + close: gtk::Button, } impl Default for InAppNotification { fn default() -> Self { let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/inapp_notif.ui"); - let overlay: gtk::Overlay = builder.get_object("notif_overlay").unwrap(); - let revealer: gtk::Revealer = builder.get_object("notif_revealer").unwrap(); - let text: gtk::Label = builder.get_object("notif_label").unwrap(); - let undo: gtk::Button = builder.get_object("undo_button").unwrap(); + let overlay: gtk::Overlay = builder.get_object("overlay").unwrap(); + let revealer: gtk::Revealer = builder.get_object("revealer").unwrap(); + let text: gtk::Label = builder.get_object("text").unwrap(); + let undo: gtk::Button = builder.get_object("undo").unwrap(); + let close: gtk::Button = builder.get_object("close").unwrap(); InAppNotification { overlay, revealer, text, undo, + close, } } } @@ -40,11 +43,9 @@ impl InAppNotification { F: FnMut() -> glib::Continue + 'static, { let notif = InAppNotification::default(); - notif.text.set_text(&text); - notif.revealer.set_reveal_child(true); - let id = timeout_add_seconds(60, callback); + let id = timeout_add_seconds(6, callback); let id = Rc::new(RefCell::new(Some(id))); // Cancel the callback @@ -66,6 +67,20 @@ impl InAppNotification { } }); + // Hide the revealer when the close button is clicked + let revealer = notif.revealer.clone(); + notif.close.connect_clicked(move |_| { + revealer.set_reveal_child(false); + }); + notif } + + // This is a seperate method cause in order to get a nice animation + // the revealer should be attached to something that will display it. + // Previouslyi we where doing it in the constructor, which had the result + // of the animation being skipped cause there was no parent widget to display it. + pub fn show(&self) { + self.revealer.set_reveal_child(true); + } } From 3423d854e1a8c343d7dc25552674db0f1057d70f Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 9 Mar 2018 16:43:13 +0200 Subject: [PATCH 13/23] ShowWidget: Change the mark_all notif wording. --- hammond-gtk/resources/gtk/show_widget.ui | 2 +- hammond-gtk/src/app.rs | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/hammond-gtk/resources/gtk/show_widget.ui b/hammond-gtk/resources/gtk/show_widget.ui index 2b54c11..6aaf5da 100644 --- a/hammond-gtk/resources/gtk/show_widget.ui +++ b/hammond-gtk/resources/gtk/show_widget.ui @@ -279,7 +279,7 @@ Tobias Bernard True True True - Mark all epiodes as Watched + Mark all episodes as listened True diff --git a/hammond-gtk/src/app.rs b/hammond-gtk/src/app.rs index 9a4e8b1..5f68a6e 100644 --- a/hammond-gtk/src/app.rs +++ b/hammond-gtk/src/app.rs @@ -162,17 +162,15 @@ impl App { Ok(Action::HeaderBarShowUpdateIndicator) => headerbar.show_update_notification(), Ok(Action::HeaderBarHideUpdateIndicator) => headerbar.hide_update_notification(), Ok(Action::MarkAllPlayerNotification(pd)) => { - let sender = sender.clone(); let callback = clone!(sender => move || { if let Err(err) = mark_all_watched(&pd, sender.clone()) { error!("Something went horribly wrong with the notif callback: {}", err); } glib::Continue(false) }); - let text = "All episodes where marked as watched."; - let sender = sender.clone(); - let notif = InAppNotification::new(text.into(), callback, sender); + let text = "Marked all episodes as listened."; + let notif = InAppNotification::new(text.into(), callback, sender.clone()); overlay.add_overlay(¬if.overlay); // We need to display the notification after the widget is added to the overlay // so there will be a nice animation. From 99bc80c15b6105153f3cc55a04044036ca0d9830 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 9 Mar 2018 17:04:11 +0200 Subject: [PATCH 14/23] ShowWidget: Add a 6px margin to the settings popup. --- hammond-gtk/resources/gtk/show_widget.ui | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hammond-gtk/resources/gtk/show_widget.ui b/hammond-gtk/resources/gtk/show_widget.ui index 6aaf5da..586f85e 100644 --- a/hammond-gtk/resources/gtk/show_widget.ui +++ b/hammond-gtk/resources/gtk/show_widget.ui @@ -273,6 +273,10 @@ Tobias Bernard True False + 6 + 6 + 6 + 6 vertical From 745ea0ca10fc7ba1b8382f78e52ba3c9b950cd7b Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 9 Mar 2018 17:14:49 +0200 Subject: [PATCH 15/23] Flatpak: Add dconf permissions. Required also in order to run the gtk+ inspecor. --- org.gnome.Hammond.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/org.gnome.Hammond.json b/org.gnome.Hammond.json index a27cc2c..86a21b9 100644 --- a/org.gnome.Hammond.json +++ b/org.gnome.Hammond.json @@ -12,6 +12,10 @@ ], "desktop-file-name-prefix" : "(Nightly) ", "finish-args" : [ + "--filesystem=xdg-run/dconf", + "--filesystem=~/.config/dconf:ro", + "--talk-name=ca.desrt.dconf", + "--env=DCONF_USER_CONFIG_DIR=.config/dconf", "--share=network", "--share=ipc", "--socket=x11", From 861492221377cf2ff70af94a849aa7a2496166e9 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 9 Mar 2018 19:36:43 +0200 Subject: [PATCH 16/23] InAppNotification: Change box margins. --- hammond-gtk/resources/gtk/inapp_notif.ui | 6 +++--- hammond-gtk/src/app.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hammond-gtk/resources/gtk/inapp_notif.ui b/hammond-gtk/resources/gtk/inapp_notif.ui index 4074fd8..2b14bd1 100644 --- a/hammond-gtk/resources/gtk/inapp_notif.ui +++ b/hammond-gtk/resources/gtk/inapp_notif.ui @@ -25,9 +25,9 @@ False center center - 6 - 6 - 12 + 6 + 6 + 6 True diff --git a/hammond-gtk/src/app.rs b/hammond-gtk/src/app.rs index 5f68a6e..a9a125b 100644 --- a/hammond-gtk/src/app.rs +++ b/hammond-gtk/src/app.rs @@ -169,7 +169,7 @@ impl App { glib::Continue(false) }); - let text = "Marked all episodes as listened."; + let text = "Marked all episodes as listened"; let notif = InAppNotification::new(text.into(), callback, sender.clone()); overlay.add_overlay(¬if.overlay); // We need to display the notification after the widget is added to the overlay From 064879c4ce9268e29d299aa1e0c60ebdfa7e62e3 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 9 Mar 2018 19:46:46 +0200 Subject: [PATCH 17/23] InAppNotification: Remove reduntant Overlay. --- hammond-gtk/resources/gtk/inapp_notif.ui | 132 +++++++++++------------ hammond-gtk/src/app.rs | 2 +- hammond-gtk/src/appnotif.rs | 5 +- 3 files changed, 68 insertions(+), 71 deletions(-) diff --git a/hammond-gtk/resources/gtk/inapp_notif.ui b/hammond-gtk/resources/gtk/inapp_notif.ui index 2b14bd1..14761b6 100644 --- a/hammond-gtk/resources/gtk/inapp_notif.ui +++ b/hammond-gtk/resources/gtk/inapp_notif.ui @@ -2,100 +2,100 @@ - + True False center start - + True False + center + center + 0 + none - + True False center center - 0 - none + 6 + 6 + 6 - + True False center center - 6 - 6 - 6 + An in-app action notification + + + False + False + 0 + + + + + True + True + False + True + center + center + none - + True False - center - center - An in-app action notification + window-close-symbolic - - False - False - 0 - - - - - True - True - False - True - center - center - none - - - True - False - window-close-symbolic - - - - - False - False - end - 1 - - - - - Undo - True - True - True - center - center - - - False - False - end - 2 - + + + False + False + end + 1 + - - + + + Undo + True + True + True + center + center + + + + False + False + end + 2 + - + + + + - - -1 - + diff --git a/hammond-gtk/src/app.rs b/hammond-gtk/src/app.rs index a9a125b..4fbdfd0 100644 --- a/hammond-gtk/src/app.rs +++ b/hammond-gtk/src/app.rs @@ -171,7 +171,7 @@ impl App { let text = "Marked all episodes as listened"; let notif = InAppNotification::new(text.into(), callback, sender.clone()); - overlay.add_overlay(¬if.overlay); + 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(); diff --git a/hammond-gtk/src/appnotif.rs b/hammond-gtk/src/appnotif.rs index a949fae..1ba012e 100644 --- a/hammond-gtk/src/appnotif.rs +++ b/hammond-gtk/src/appnotif.rs @@ -10,8 +10,7 @@ use std::sync::mpsc::Sender; #[derive(Debug, Clone)] pub struct InAppNotification { - pub overlay: gtk::Overlay, - revealer: gtk::Revealer, + pub revealer: gtk::Revealer, text: gtk::Label, undo: gtk::Button, close: gtk::Button, @@ -21,14 +20,12 @@ impl Default for InAppNotification { fn default() -> Self { let builder = gtk::Builder::new_from_resource("/org/gnome/hammond/gtk/inapp_notif.ui"); - let overlay: gtk::Overlay = builder.get_object("overlay").unwrap(); let revealer: gtk::Revealer = builder.get_object("revealer").unwrap(); let text: gtk::Label = builder.get_object("text").unwrap(); let undo: gtk::Button = builder.get_object("undo").unwrap(); let close: gtk::Button = builder.get_object("close").unwrap(); InAppNotification { - overlay, revealer, text, undo, From dbbb4e589e693a579b4d326176e275b194a45aec Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Fri, 9 Mar 2018 20:24:28 +0200 Subject: [PATCH 18/23] InAppNotification: Fix autohiding after the callback is executed. --- hammond-gtk/src/appnotif.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hammond-gtk/src/appnotif.rs b/hammond-gtk/src/appnotif.rs index 1ba012e..07291a7 100644 --- a/hammond-gtk/src/appnotif.rs +++ b/hammond-gtk/src/appnotif.rs @@ -35,14 +35,18 @@ impl Default for InAppNotification { } impl InAppNotification { - pub fn new(text: String, callback: F, sender: Sender) -> Self + pub fn new(text: String, mut callback: F, sender: Sender) -> Self where F: FnMut() -> glib::Continue + 'static, { let notif = InAppNotification::default(); notif.text.set_text(&text); - let id = timeout_add_seconds(6, callback); + let revealer = notif.revealer.clone(); + let id = timeout_add_seconds(6, move || { + revealer.set_reveal_child(false); + callback() + }); let id = Rc::new(RefCell::new(Some(id))); // Cancel the callback From 285291b2ed735c557f2348fe52447c0fd893b09d Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Mon, 12 Mar 2018 03:35:07 +0200 Subject: [PATCH 19/23] Ignore tests that need access to the host system. --- hammond-data/src/utils.rs | 2 ++ hammond-gtk/src/manager.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/hammond-data/src/utils.rs b/hammond-data/src/utils.rs index b19e76c..29138f1 100644 --- a/hammond-data/src/utils.rs +++ b/hammond-data/src/utils.rs @@ -318,6 +318,8 @@ mod tests { } #[test] + // This test needs access to local system so we ignore it by default. + #[ignore] fn test_get_dl_folder() { let foo_ = format!("{}/{}", DL_DIR.to_str().unwrap(), "foo"); assert_eq!(get_download_folder("foo").unwrap(), foo_); diff --git a/hammond-gtk/src/manager.rs b/hammond-gtk/src/manager.rs index 1c86a1e..c4ead04 100644 --- a/hammond-gtk/src/manager.rs +++ b/hammond-gtk/src/manager.rs @@ -174,6 +174,8 @@ mod tests { } #[test] + // This test needs access to local system so we ignore it by default. + #[ignore] fn test_dl_steal_the_stars() { let url = "https://web.archive.org/web/20180120104957if_/https://rss.art19.com/steal-the-stars"; From 9da2414e8b08d9b13612085c986fba3779944b9a Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Mon, 12 Mar 2018 06:48:05 +0200 Subject: [PATCH 20/23] Initial implementation of an itunes_to_rss url resolver. #49 --- Cargo.lock | 3 +++ hammond-gtk/Cargo.toml | 3 +++ hammond-gtk/src/main.rs | 3 +++ hammond-gtk/src/utils.rs | 57 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 1c9b099..c6f8ab0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -707,7 +707,10 @@ dependencies = [ "loggerv 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "reqwest 0.8.5 (registry+https://github.com/rust-lang/crates.io-index)", "send-cell 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/hammond-gtk/Cargo.toml b/hammond-gtk/Cargo.toml index 2d11674..a05fcad 100644 --- a/hammond-gtk/Cargo.toml +++ b/hammond-gtk/Cargo.toml @@ -22,6 +22,9 @@ send-cell = "0.1.2" url = "1.6.0" failure = "0.1.1" failure_derive = "0.1.1" +regex = "0.2.6" +reqwest = "0.8.5" +serde_json = "1.0" [dependencies.gtk] features = ["v3_22"] diff --git a/hammond-gtk/src/main.rs b/hammond-gtk/src/main.rs index 013fcdc..ab85959 100644 --- a/hammond-gtk/src/main.rs +++ b/hammond-gtk/src/main.rs @@ -25,7 +25,10 @@ extern crate hammond_downloader; extern crate humansize; extern crate loggerv; extern crate open; +extern crate regex; +extern crate reqwest; extern crate send_cell; +extern crate serde_json; extern crate url; // extern crate rayon; diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index 26ee2be..7fa8682 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -2,7 +2,10 @@ use failure::Error; use gdk_pixbuf::Pixbuf; +use regex::Regex; +use reqwest; use send_cell::SendCell; +use serde_json::Value; // use hammond_data::feed; use hammond_data::{PodcastCoverQuery, Source}; @@ -106,6 +109,34 @@ pub fn get_pixbuf_from_path(pd: &PodcastCoverQuery, size: u32) -> Result Result` +pub fn itunes_to_rss(url: &str) -> Result { + let id = itunes_id_from_url(url).ok_or_else(|| format_err!("Failed to find an Itunes ID."))?; + lookup_id(id) +} + +fn itunes_id_from_url(url: &str) -> Option { + lazy_static! { + static ref RE: Regex = Regex::new(r"/id([0-9]+)").unwrap(); + } + + // Get the itunes id from the url + let foo = RE.captures_iter(url).nth(0)?.get(1)?.as_str(); + // Parse it to a u32, this *should* never fail + foo.parse::().ok() +} + +fn lookup_id(id: u32) -> Result { + let url = format!("https://itunes.apple.com/lookup?id={}&entity=podcast", id); + let req: Value = reqwest::get(&url)?.json()?; + // FIXME: First time using serde, this could be done better and avoid using [] for indexing. + let feedurl = req["results"][0]["feedUrl"] + .as_str() + .ok_or_else(|| format_err!("Failed to get url from itunes response"))? + .into(); + Ok(feedurl) +} + #[cfg(test)] mod tests { use super::*; @@ -129,4 +160,30 @@ mod tests { let pxbuf = get_pixbuf_from_path(&pd.into(), 256); assert!(pxbuf.is_ok()); } + + #[test] + fn test_itunes_to_rss() { + let itunes_url = "https://itunes.apple.com/podcast/id1195206601"; + let rss_url = String::from("http://feeds.feedburner.com/InterceptedWithJeremyScahill"); + + assert_eq!(rss_url, itunes_to_rss(itunes_url).unwrap()); + } + + #[test] + fn test_itunes_id() { + let itunes_url = "https://itunes.apple.com/podcast/id1195206601"; + let id = 1195206601; + + assert_eq!(id, itunes_id_from_url(itunes_url).unwrap()); + } + + #[test] + fn test_itunes_lookup_id() { + let id = 1195206601; + + assert_eq!( + "http://feeds.feedburner.com/InterceptedWithJeremyScahill", + lookup_id(id).unwrap() + ); + } } From b87c331b12da9268e4cd6fbaf00df7bb1e0bb766 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Mon, 12 Mar 2018 07:28:09 +0200 Subject: [PATCH 21/23] Make the itunes_resolver functions inlined. --- hammond-gtk/src/utils.rs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index 7fa8682..b0a93fe 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -109,12 +109,14 @@ pub fn get_pixbuf_from_path(pd: &PodcastCoverQuery, size: u32) -> Result Result` pub fn itunes_to_rss(url: &str) -> Result { let id = itunes_id_from_url(url).ok_or_else(|| format_err!("Failed to find an Itunes ID."))?; lookup_id(id) } +#[inline] fn itunes_id_from_url(url: &str) -> Option { lazy_static! { static ref RE: Regex = Regex::new(r"/id([0-9]+)").unwrap(); @@ -126,6 +128,7 @@ fn itunes_id_from_url(url: &str) -> Option { foo.parse::().ok() } +#[inline] fn lookup_id(id: u32) -> Result { let url = format!("https://itunes.apple.com/lookup?id={}&entity=podcast", id); let req: Value = reqwest::get(&url)?.json()?; @@ -165,25 +168,20 @@ mod tests { fn test_itunes_to_rss() { let itunes_url = "https://itunes.apple.com/podcast/id1195206601"; let rss_url = String::from("http://feeds.feedburner.com/InterceptedWithJeremyScahill"); - assert_eq!(rss_url, itunes_to_rss(itunes_url).unwrap()); } #[test] fn test_itunes_id() { - let itunes_url = "https://itunes.apple.com/podcast/id1195206601"; let id = 1195206601; - + let itunes_url = "https://itunes.apple.com/podcast/id1195206601"; assert_eq!(id, itunes_id_from_url(itunes_url).unwrap()); } #[test] fn test_itunes_lookup_id() { let id = 1195206601; - - assert_eq!( - "http://feeds.feedburner.com/InterceptedWithJeremyScahill", - lookup_id(id).unwrap() - ); + let rss_url = "http://feeds.feedburner.com/InterceptedWithJeremyScahill"; + assert_eq!(rss_url, lookup_id(id).unwrap()); } } From 8a18630ae59227565112be795b3dbe48d7c710a7 Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Mon, 12 Mar 2018 20:49:02 +0200 Subject: [PATCH 22/23] Initial integration of the itunes resolver with the Add button. --- hammond-gtk/src/headerbar.rs | 12 ++++++++++++ hammond-gtk/src/utils.rs | 8 +++----- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/hammond-gtk/src/headerbar.rs b/hammond-gtk/src/headerbar.rs index 15c6b83..3b5bd50 100644 --- a/hammond-gtk/src/headerbar.rs +++ b/hammond-gtk/src/headerbar.rs @@ -12,6 +12,7 @@ use std::sync::mpsc::Sender; use app::Action; use stacks::Content; +use utils::itunes_to_rss; #[derive(Debug, Clone)] pub struct Header { @@ -154,8 +155,18 @@ impl Header { } } +// FIXME: THIS ALSO SUCKS! fn on_add_bttn_clicked(entry: >k::Entry, sender: Sender) -> Result<(), Error> { let url = entry.get_text().unwrap_or_default(); + let url = if url.contains("itunes.com") || url.contains("apple.com") { + info!("Detected itunes url."); + let foo = itunes_to_rss(&url)?; + info!("Resolved to {}", foo); + foo + } else { + url.to_owned() + }; + let source = Source::from_url(&url).context("Failed to convert url to a Source entry.")?; entry.set_text(""); @@ -165,6 +176,7 @@ fn on_add_bttn_clicked(entry: >k::Entry, sender: Sender) -> Result<(), Ok(()) } +// FIXME: THIS SUCKS! fn on_url_change( entry: >k::Entry, result: >k::Label, diff --git a/hammond-gtk/src/utils.rs b/hammond-gtk/src/utils.rs index b0a93fe..5d94e7a 100644 --- a/hammond-gtk/src/utils.rs +++ b/hammond-gtk/src/utils.rs @@ -133,11 +133,9 @@ fn lookup_id(id: u32) -> Result { let url = format!("https://itunes.apple.com/lookup?id={}&entity=podcast", id); let req: Value = reqwest::get(&url)?.json()?; // FIXME: First time using serde, this could be done better and avoid using [] for indexing. - let feedurl = req["results"][0]["feedUrl"] - .as_str() - .ok_or_else(|| format_err!("Failed to get url from itunes response"))? - .into(); - Ok(feedurl) + let feedurl = req["results"][0]["feedUrl"].as_str(); + let feedurl = feedurl.ok_or_else(|| format_err!("Failed to get url from itunes response"))?; + Ok(feedurl.into()) } #[cfg(test)] From 3dcc20ae86c1c1f44ef1fdd714d0d638262e68eb Mon Sep 17 00:00:00 2001 From: Jordan Petridis Date: Mon, 12 Mar 2018 21:14:12 +0200 Subject: [PATCH 23/23] Update changelog. --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c3caaa5..cb7dd84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +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] +* Ability to mark all episodes of a Show as watched. [#47](https://gitlab.gnome.org/alatiera/Hammond/issues/47) +* Now you are able to subscribe to itunes™ podcasts by using the itunes link of the show.[#49](https://gitlab.gnome.org/alatiera/Hammond/issues/49) ## [0.3.0] - 2018-02-11