Factor out the In-app notification into something generic.

This commit is contained in:
Jordan Petridis 2018-03-09 01:21:54 +02:00
parent 7de118adeb
commit d1821163c2
No known key found for this signature in database
GPG Key ID: CEABAD9F5683B9A6
6 changed files with 261 additions and 233 deletions

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.21.0 -->
<interface>
<requires lib="gtk+" version="3.20"/>
<object class="GtkOverlay" id="inapp_notif_overlay">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">start</property>
<child>
<object class="GtkRevealer" id="notif_revealer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<child>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="notif_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="label" translatable="yes">An in-app action notification</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="undo_button">
<property name="label" translatable="yes">Undo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
<style>
<class name="app-notification"/>
</style>
</object>
</child>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child>
</object>
</interface>

View File

@ -68,250 +68,167 @@ Tobias Bernard
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkOverlay"> <object class="GtkBox">
<property name="width_request">600</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_left">32</property>
<property name="margin_right">32</property>
<property name="margin_top">32</property>
<property name="margin_bottom">32</property>
<property name="hexpand">False</property>
<property name="orientation">vertical</property>
<property name="spacing">24</property>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="width_request">600</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="margin_left">32</property> <property name="valign">center</property>
<property name="margin_right">32</property> <property name="spacing">12</property>
<property name="margin_top">32</property> <child>
<property name="margin_bottom">32</property> <object class="GtkImage" id="cover">
<property name="hexpand">False</property> <property name="visible">True</property>
<property name="orientation">vertical</property> <property name="can_focus">False</property>
<property name="spacing">24</property> <property name="pixel_size">128</property>
<property name="icon_name">image-x-generic-symbolic</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="valign">center</property> <property name="valign">end</property>
<property name="spacing">12</property> <property name="hexpand">True</property>
<child> <property name="orientation">vertical</property>
<object class="GtkImage" id="cover"> <property name="spacing">6</property>
<child type="center">
<object class="GtkLabel" id="description">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="pixel_size">128</property> <property name="halign">start</property>
<property name="icon_name">image-x-generic-symbolic</property> <property name="valign">end</property>
<property name="label" translatable="yes">Show description</property>
<property name="wrap">True</property>
<property name="wrap_mode">word-char</property>
<property name="max_width_chars">100</property>
<attributes>
<attribute name="weight" value="medium"/>
</attributes>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">False</property> <property name="fill">False</property>
<property name="position">0</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkBox"> <object class="GtkBox">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="valign">end</property>
<property name="hexpand">True</property>
<property name="orientation">vertical</property>
<property name="spacing">6</property> <property name="spacing">6</property>
<child type="center"> <child>
<object class="GtkLabel" id="description"> <object class="GtkMenuButton" id="settings_button">
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">True</property>
<property name="halign">start</property> <property name="receives_default">True</property>
<property name="valign">end</property> <child>
<property name="label" translatable="yes">Show description</property> <object class="GtkImage">
<property name="wrap">True</property> <property name="visible">True</property>
<property name="wrap_mode">word-char</property> <property name="can_focus">False</property>
<property name="max_width_chars">100</property> <property name="halign">center</property>
<attributes> <property name="valign">center</property>
<attribute name="weight" value="medium"/> <property name="icon_name">emblem-system-symbolic</property>
</attributes> </object>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">False</property> <property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="link_button">
<property name="label" translatable="yes">Website</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkBox"> <object class="GtkButton" id="unsub_button">
<property name="label" translatable="yes">Unsubscribe</property>
<property name="visible">True</property> <property name="visible">True</property>
<property name="can_focus">False</property> <property name="can_focus">True</property>
<property name="spacing">6</property> <property name="receives_default">True</property>
<child> <property name="halign">center</property>
<object class="GtkMenuButton" id="settings_button"> <property name="valign">center</property>
<property name="visible">True</property> <style>
<property name="can_focus">True</property> <class name="destructive-action"/>
<property name="receives_default">True</property> </style>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="icon_name">emblem-system-symbolic</property>
</object>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="link_button">
<property name="label" translatable="yes">Website</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="unsub_button">
<property name="label" translatable="yes">Unsubscribe</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
<style>
<class name="destructive-action"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="padding">5</property>
<property name="pack_type">end</property>
<property name="position">2</property>
</packing>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">False</property> <property name="fill">True</property>
<property name="padding">5</property>
<property name="pack_type">end</property> <property name="pack_type">end</property>
<property name="position">0</property> <property name="position">2</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">True</property> <property name="fill">False</property>
<property name="position">1</property> <property name="pack_type">end</property>
<property name="position">0</property>
</packing> </packing>
</child> </child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
<property name="fill">False</property> <property name="fill">True</property>
<property name="position">1</property> <property name="position">1</property>
</packing> </packing>
</child> </child>
<child>
<object class="GtkFrame" id="episodes">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="hexpand">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child>
<placeholder/>
</child>
<child type="label_item">
<placeholder/>
</child>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child>
</object> </object>
<packing> <packing>
<property name="index">-1</property> <property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing> </packing>
</child> </child>
<child type="overlay"> <child>
<object class="GtkOverlay" id="inapp_notif_overlay"> <object class="GtkFrame" id="episodes">
<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="hexpand">True</property>
<property name="valign">start</property> <property name="label_xalign">0</property>
<property name="shadow_type">in</property>
<child> <child>
<object class="GtkRevealer" id="notif_revealer"> <placeholder/>
<property name="visible">True</property> </child>
<property name="can_focus">False</property> <child type="label_item">
<child> <placeholder/>
<object class="GtkFrame">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="label_xalign">0</property>
<property name="shadow_type">none</property>
<child>
<object class="GtkBox">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="margin_top">6</property>
<property name="margin_bottom">6</property>
<property name="spacing">12</property>
<child>
<object class="GtkLabel" id="notif_label">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="label" translatable="yes">An in-app action notification</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkButton" id="undo_button">
<property name="label" translatable="yes">Undo</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="halign">center</property>
<property name="valign">center</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="pack_type">end</property>
<property name="position">1</property>
</packing>
</child>
</object>
</child>
<child type="label_item">
<placeholder/>
</child>
<style>
<class name="app-notification"/>
</style>
</object>
</child>
</object>
<packing>
<property name="index">-1</property>
</packing>
</child> </child>
</object> </object>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">2</property>
</packing>
</child> </child>
</object> </object>
<packing> <packing>

View File

@ -39,6 +39,7 @@ pub enum Action {
pub struct App { pub struct App {
app_instance: gtk::Application, app_instance: gtk::Application,
window: gtk::Window, window: gtk::Window,
overlay: gtk::Overlay,
header: Arc<Header>, header: Arc<Header>,
content: Arc<Content>, content: Arc<Content>,
receiver: Receiver<Action>, receiver: Receiver<Action>,
@ -73,12 +74,17 @@ impl App {
// Create the headerbar // Create the headerbar
let header = Arc::new(Header::new(&content, &window, sender.clone())); let header = Arc::new(Header::new(&content, &window, sender.clone()));
// Add the content main stack to the window. // Add the content main stack to the overlay.
window.add(&content.get_stack()); let overlay = gtk::Overlay::new();
overlay.add(&content.get_stack());
// Add the overlay to the main window
window.add(&overlay);
App { App {
app_instance: application, app_instance: application,
window, window,
overlay,
header, header,
content, content,
receiver, receiver,

View File

@ -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<F>(text: &str, callback: F, sender: Sender<Action>) -> 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
}
}

View File

@ -65,6 +65,7 @@ pub mod app;
pub mod utils; pub mod utils;
pub mod manager; pub mod manager;
pub mod static_resource; pub mod static_resource;
pub mod appnotif;
use app::App; use app::App;

View File

@ -10,11 +10,10 @@ 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;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc; use std::sync::Arc;
use std::sync::mpsc::Sender; use std::sync::mpsc::Sender;
use std::thread; use std::thread;
@ -29,9 +28,6 @@ pub struct ShowWidget {
settings: gtk::MenuButton, settings: gtk::MenuButton,
unsub: gtk::Button, unsub: gtk::Button,
episodes: gtk::Frame, episodes: gtk::Frame,
notif: gtk::Revealer,
notif_label: gtk::Label,
notif_undo: gtk::Button,
} }
impl Default for ShowWidget { impl Default for ShowWidget {
@ -47,10 +43,6 @@ impl Default for ShowWidget {
let link: gtk::Button = builder.get_object("link_button").unwrap(); let link: gtk::Button = builder.get_object("link_button").unwrap();
let settings: gtk::MenuButton = builder.get_object("settings_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 { ShowWidget {
container, container,
scrolled_window, scrolled_window,
@ -60,9 +52,6 @@ impl Default for ShowWidget {
link, link,
settings, settings,
episodes, episodes,
notif,
notif_label,
notif_undo,
} }
} }
} }
@ -107,16 +96,10 @@ impl ShowWidget {
let show_menu: gtk::Popover = builder.get_object("show_menu").unwrap(); let show_menu: gtk::Popover = builder.get_object("show_menu").unwrap();
let mark_all: gtk::ModelButton = builder.get_object("mark_all_watched").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(); let episodes = self.episodes.clone();
mark_all.connect_clicked(clone!(pd, sender => move |_| { mark_all.connect_clicked(clone!(pd, sender => move |_| {
on_played_button_clicked( on_played_button_clicked(
pd.clone(), pd.clone(),
&notif,
&notif_label,
&notif_undo,
&episodes, &episodes,
sender.clone() sender.clone()
) )
@ -176,21 +159,13 @@ fn on_unsub_button_clicked(
Ok(()) Ok(())
} }
fn on_played_button_clicked( fn on_played_button_clicked(pd: Arc<Podcast>, episodes: &gtk::Frame, sender: Sender<Action>) {
pd: Arc<Podcast>,
notif: &gtk::Revealer,
label: &gtk::Label,
undo: &gtk::Button,
episodes: &gtk::Frame,
sender: Sender<Action>,
) {
if dim_titles(episodes).is_none() { if dim_titles(episodes).is_none() {
error!("Something went horribly wrong when dimming the titles."); error!("Something went horribly wrong when dimming the titles.");
warn!("RUN WHILE YOU STILL CAN!"); warn!("RUN WHILE YOU STILL CAN!");
} }
label.set_text("All episodes where marked as watched."); let text = "All episodes where marked as watched.";
notif.set_reveal_child(true);
// Set up the callback // Set up the callback
let callback = clone!(sender => move || { let callback = clone!(sender => move || {
@ -199,23 +174,7 @@ fn on_played_button_clicked(
} }
glib::Continue(false) glib::Continue(false)
}); });
let id = timeout_add_seconds(10, callback); let _notif = InAppNotification::new(text, callback, sender);
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)
}
}));
} }
fn wrap(pd: &Podcast, sender: Sender<Action>) -> Result<(), Error> { fn wrap(pd: &Podcast, sender: Sender<Action>) -> Result<(), Error> {