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.
This commit is contained in:
Jordan Petridis 2018-03-09 14:53:13 +02:00
parent d1821163c2
commit 82988b6011
No known key found for this signature in database
GPG Key ID: CEABAD9F5683B9A6
6 changed files with 34 additions and 20 deletions

View File

@ -2,7 +2,7 @@
<!-- Generated with glade 3.21.0 --> <!-- Generated with glade 3.21.0 -->
<interface> <interface>
<requires lib="gtk+" version="3.20"/> <requires lib="gtk+" version="3.20"/>
<object class="GtkOverlay" id="inapp_notif_overlay"> <object class="GtkOverlay" id="notif_overlay">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="halign">center</property> <property name="halign">center</property>

View File

@ -9,6 +9,7 @@
<file preprocess="xml-stripblanks">gtk/shows_view.ui</file> <file preprocess="xml-stripblanks">gtk/shows_view.ui</file>
<file preprocess="xml-stripblanks">gtk/shows_child.ui</file> <file preprocess="xml-stripblanks">gtk/shows_child.ui</file>
<file preprocess="xml-stripblanks">gtk/headerbar.ui</file> <file preprocess="xml-stripblanks">gtk/headerbar.ui</file>
<file preprocess="xml-stripblanks">gtk/inapp_notif.ui</file>
<file compressed="true">gtk/style.css</file> <file compressed="true">gtk/style.css</file>
</gresource> </gresource>
</gresources> </gresources>

View File

@ -8,9 +8,11 @@ use gtk::prelude::*;
use hammond_data::{Podcast, Source}; use hammond_data::{Podcast, Source};
use hammond_data::utils::checkup; use hammond_data::utils::checkup;
use appnotif::*;
use headerbar::Header; use headerbar::Header;
use stacks::Content; use stacks::Content;
use utils; use utils;
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};
@ -33,6 +35,7 @@ pub enum Action {
HeaderBarNormal, HeaderBarNormal,
HeaderBarShowUpdateIndicator, HeaderBarShowUpdateIndicator,
HeaderBarHideUpdateIndicator, HeaderBarHideUpdateIndicator,
MarkAllPlayerNotification(Arc<Podcast>),
} }
#[derive(Debug)] #[derive(Debug)]
@ -92,7 +95,7 @@ impl App {
} }
} }
pub fn setup_timed_callbacks(&self) { fn setup_timed_callbacks(&self) {
let sender = self.sender.clone(); let sender = self.sender.clone();
// Update the feeds right after the Application is initialized. // Update the feeds right after the Application is initialized.
gtk::timeout_add_seconds(2, move || { gtk::timeout_add_seconds(2, move || {
@ -128,6 +131,7 @@ impl App {
let content = self.content.clone(); let content = self.content.clone();
let headerbar = self.header.clone(); let headerbar = self.header.clone();
let sender = self.sender.clone(); let sender = self.sender.clone();
let overlay = self.overlay.clone();
let receiver = self.receiver; let receiver = self.receiver;
gtk::idle_add(move || { gtk::idle_add(move || {
match receiver.recv_timeout(Duration::from_millis(10)) { match receiver.recv_timeout(Duration::from_millis(10)) {
@ -157,6 +161,21 @@ impl App {
Ok(Action::HeaderBarNormal) => headerbar.switch_to_normal(), Ok(Action::HeaderBarNormal) => headerbar.switch_to_normal(),
Ok(Action::HeaderBarShowUpdateIndicator) => headerbar.show_update_notification(), Ok(Action::HeaderBarShowUpdateIndicator) => headerbar.show_update_notification(),
Ok(Action::HeaderBarHideUpdateIndicator) => headerbar.hide_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(&notif.overlay);
overlay.show_all();
}
Err(_) => (), Err(_) => (),
} }

View File

@ -10,6 +10,7 @@ use std::sync::mpsc::Sender;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct InAppNotification { pub struct InAppNotification {
pub overlay: gtk::Overlay,
revealer: gtk::Revealer, revealer: gtk::Revealer,
text: gtk::Label, text: gtk::Label,
undo: gtk::Button, undo: gtk::Button,
@ -17,13 +18,15 @@ pub struct InAppNotification {
impl Default for InAppNotification { impl Default for InAppNotification {
fn default() -> Self { 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 revealer: gtk::Revealer = builder.get_object("notif_revealer").unwrap();
let text: gtk::Label = builder.get_object("notif_label").unwrap(); let text: gtk::Label = builder.get_object("notif_label").unwrap();
let undo: gtk::Button = builder.get_object("undo_button").unwrap(); let undo: gtk::Button = builder.get_object("undo_button").unwrap();
InAppNotification { InAppNotification {
overlay,
revealer, revealer,
text, text,
undo, undo,
@ -32,16 +35,16 @@ impl Default for InAppNotification {
} }
impl InAppNotification { impl InAppNotification {
pub fn new<F>(text: &str, callback: F, sender: Sender<Action>) -> Self pub fn new<F>(text: String, callback: F, sender: Sender<Action>) -> Self
where where
F: FnMut() -> Continue + 'static, F: FnMut() -> glib::Continue + 'static,
{ {
let notif = InAppNotification::default(); let notif = InAppNotification::default();
notif.text.set_text(text); notif.text.set_text(&text);
notif.revealer.set_reveal_child(true); 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))); let id = Rc::new(RefCell::new(Some(id)));
// Cancel the callback // Cancel the callback

View File

@ -3,3 +3,4 @@ mod episode;
pub use self::episode::EpisodeWidget; pub use self::episode::EpisodeWidget;
pub use self::show::ShowWidget; pub use self::show::ShowWidget;
pub use self::show::mark_all_watched;

View File

@ -1,6 +1,6 @@
use dissolve; use dissolve;
use failure::Error; use failure::Error;
use glib; // use glib;
use gtk; use gtk;
use gtk::prelude::*; use gtk::prelude::*;
use open; use open;
@ -10,7 +10,6 @@ use hammond_data::dbqueries;
use hammond_data::utils::{delete_show, replace_extra_spaces}; use hammond_data::utils::{delete_show, replace_extra_spaces};
use app::Action; use app::Action;
use appnotif::InAppNotification;
use utils::get_pixbuf_from_path; use utils::get_pixbuf_from_path;
use widgets::episode::episodes_listbox; use widgets::episode::episodes_listbox;
@ -165,19 +164,10 @@ fn on_played_button_clicked(pd: Arc<Podcast>, episodes: &gtk::Frame, sender: Sen
warn!("RUN WHILE YOU STILL CAN!"); warn!("RUN WHILE YOU STILL CAN!");
} }
let text = "All episodes where marked as watched."; sender.send(Action::MarkAllPlayerNotification(pd)).unwrap();
// 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);
} }
fn wrap(pd: &Podcast, sender: Sender<Action>) -> Result<(), Error> { pub fn mark_all_watched(pd: &Podcast, sender: Sender<Action>) -> Result<(), Error> {
dbqueries::update_none_to_played_now(pd)?; dbqueries::update_none_to_played_now(pd)?;
sender.send(Action::RefreshWidgetIfVis)?; sender.send(Action::RefreshWidgetIfVis)?;
sender.send(Action::RefreshEpisodesView)?; sender.send(Action::RefreshEpisodesView)?;