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:
parent
d1821163c2
commit
82988b6011
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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(¬if.overlay);
|
||||||
|
overlay.show_all();
|
||||||
|
}
|
||||||
Err(_) => (),
|
Err(_) => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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: >k::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)?;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user