Implement in-app notifications. Bug 774442.
Implemented it for the mail sent-notification. Signed-off-by: Niels De Graef <nielsdegraef@gmail.com>
This commit is contained in:
parent
17551d17f6
commit
c72d7b28ac
11 changed files with 271 additions and 114 deletions
|
|
@ -76,6 +76,7 @@ src/client/folder-list/folder-list-inboxes-branch.vala
|
|||
src/client/folder-list/folder-list-search-branch.vala
|
||||
src/client/folder-list/folder-list-special-grouping.vala
|
||||
src/client/folder-list/folder-list-tree.vala
|
||||
src/client/notification/in-app-notification.vala
|
||||
src/client/notification/libmessagingmenu.vala
|
||||
src/client/notification/libnotify.vala
|
||||
src/client/notification/new-messages-indicator.vala
|
||||
|
|
@ -421,6 +422,7 @@ ui/find_bar.glade
|
|||
ui/folder-popover.ui
|
||||
ui/gtk/help-overlay.ui
|
||||
ui/gtk/menus.ui
|
||||
ui/in-app-notification.ui
|
||||
ui/login.glade
|
||||
ui/main-toolbar.ui
|
||||
ui/main-toolbar-menus.ui
|
||||
|
|
|
|||
|
|
@ -403,6 +403,7 @@ client/folder-list/folder-list-inbox-folder-entry.vala
|
|||
client/folder-list/folder-list-search-branch.vala
|
||||
client/folder-list/folder-list-special-grouping.vala
|
||||
|
||||
client/notification/in-app-notification.vala
|
||||
client/notification/libmessagingmenu.vala
|
||||
client/notification/libnotify.vala
|
||||
client/notification/new-messages-indicator.vala
|
||||
|
|
|
|||
|
|
@ -2713,6 +2713,13 @@ public class GearyController : Geary.BaseObject {
|
|||
}
|
||||
|
||||
private void on_sent(Geary.RFC822.Message rfc822) {
|
||||
// Translators: The label for an in-app notification. The
|
||||
// string substitution is a list of recipients of the email.
|
||||
string message = _(
|
||||
"Successfully sent mail to %s."
|
||||
).printf(EmailUtil.to_short_recipient_display(rfc822.to));
|
||||
InAppNotification notification = new InAppNotification(message);
|
||||
this.main_window.add_notification(notification);
|
||||
Libnotify.play_sound("message-sent-email");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
|||
private Gtk.Box conversation_box;
|
||||
[GtkChild]
|
||||
private Gtk.ScrolledWindow conversation_list_scrolled;
|
||||
[GtkChild]
|
||||
private Gtk.Overlay overlay;
|
||||
|
||||
// This is a frame so users can use F6/Shift-F6 to get to it
|
||||
[GtkChild]
|
||||
|
|
@ -170,6 +172,11 @@ public class MainWindow : Gtk.ApplicationWindow, Geary.BaseInterface {
|
|||
}
|
||||
}
|
||||
|
||||
public void add_notification(InAppNotification notification) {
|
||||
this.overlay.add_overlay(notification);
|
||||
notification.show();
|
||||
}
|
||||
|
||||
private void set_styling() {
|
||||
Gtk.CssProvider provider = new Gtk.CssProvider();
|
||||
Gtk.StyleContext.add_provider_for_screen(Gdk.Display.get_default().get_default_screen(),
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ geary_client_vala_sources = files(
|
|||
'folder-list/folder-list-search-branch.vala',
|
||||
'folder-list/folder-list-special-grouping.vala',
|
||||
|
||||
'notification/in-app-notification.vala',
|
||||
'notification/libmessagingmenu.vala',
|
||||
'notification/libnotify.vala',
|
||||
'notification/new-messages-indicator.vala',
|
||||
|
|
|
|||
66
src/client/notification/in-app-notification.vala
Normal file
66
src/client/notification/in-app-notification.vala
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/* Copyright 2017 Software Freedom Conservancy Inc.
|
||||
*
|
||||
* This software is licensed under the GNU Lesser General Public License
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents an in-app notification.
|
||||
*
|
||||
* Following the GNOME HIG, it should only contain a label and maybe a button.
|
||||
*/
|
||||
[GtkTemplate (ui = "/org/gnome/Geary/in-app-notification.ui")]
|
||||
public class InAppNotification : Gtk.Revealer {
|
||||
|
||||
/** Length of the default timeout to close the notification. */
|
||||
public const uint DEFAULT_KEEPALIVE = 5;
|
||||
|
||||
[GtkChild]
|
||||
private Gtk.Label message_label;
|
||||
[GtkChild]
|
||||
private Gtk.Button action_button;
|
||||
|
||||
/**
|
||||
* Creates an in-app notification.
|
||||
*
|
||||
* @param message The messag that should be displayed.
|
||||
* @param keepalive The amount of seconds that the notification should stay visible.
|
||||
*/
|
||||
public InAppNotification(string message, uint keepalive = DEFAULT_KEEPALIVE) {
|
||||
this.transition_type = Gtk.RevealerTransitionType.SLIDE_DOWN;
|
||||
this.message_label.label = message;
|
||||
|
||||
// Close after the given amount of time.
|
||||
Timeout.add_seconds(keepalive, () => { close(); return false; });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a button for the notification.
|
||||
*/
|
||||
public void set_button(string label, string action_name) {
|
||||
this.action_button.visible = true;
|
||||
this.action_button.label = label;
|
||||
this.action_button.action_name = action_name;
|
||||
}
|
||||
|
||||
public override void show() {
|
||||
base.show();
|
||||
this.reveal_child = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the in-app notification.
|
||||
*/
|
||||
[GtkCallback]
|
||||
public void close() {
|
||||
// Allows for the disappearing transition
|
||||
this.reveal_child = false;
|
||||
}
|
||||
|
||||
// Make sure the notification gets destroyed after closing.
|
||||
[GtkCallback]
|
||||
private void on_child_revealed(Object src, ParamSpec p) {
|
||||
if (!this.child_revealed)
|
||||
destroy();
|
||||
}
|
||||
}
|
||||
|
|
@ -30,5 +30,40 @@ public string strip_subject_prefixes(Geary.Email email) {
|
|||
return !Geary.String.is_empty(cleaned) ? cleaned : _("(no subject)");
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Returns a shortened recipient list suitable for display.
|
||||
*
|
||||
* This is useful in case there are a lot of recipients, or there
|
||||
* is little room for the display.
|
||||
*
|
||||
* @return a string containing at least the first mailbox
|
||||
* serialised by {@link MailboxAddress.to_short_display}, if the
|
||||
* list contains more mailboxes then an indication of how many
|
||||
* additional are present.
|
||||
*/
|
||||
public string to_short_recipient_display(Geary.RFC822.MailboxAddresses mailboxes) {
|
||||
if (mailboxes.size == 0) {
|
||||
// Translators: This is shown for displaying a list of
|
||||
// email recipients that happens to be empty,
|
||||
// i.e. contains no email addresses.
|
||||
return _("(No recipients)");
|
||||
}
|
||||
|
||||
// Always mention the first recipient
|
||||
string first_recipient = mailboxes.get(0).to_short_display();
|
||||
if (mailboxes.size == 1)
|
||||
return first_recipient;
|
||||
|
||||
// Translators: This is used for displaying a short list of
|
||||
// email recipients lists with two or more addresses. The
|
||||
// first (string) substitution is address of the first, the
|
||||
// second substitution is the number of n - 1 remaining
|
||||
// recipients.
|
||||
return GLib.ngettext(
|
||||
"%s and %d other",
|
||||
"%s and %d others",
|
||||
mailboxes.size - 1
|
||||
).printf(first_recipient, mailboxes.size - 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ set(RESOURCE_LIST
|
|||
STRIPBLANKS "folder-popover.ui"
|
||||
STRIPBLANKS "gtk/help-overlay.ui"
|
||||
STRIPBLANKS "gtk/menus.ui"
|
||||
STRIPBLANKS "in-app-notification.ui"
|
||||
STRIPBLANKS "login.glade"
|
||||
STRIPBLANKS "main-toolbar.ui"
|
||||
STRIPBLANKS "main-toolbar-menus.ui"
|
||||
|
|
|
|||
48
ui/in-app-notification.ui
Normal file
48
ui/in-app-notification.ui
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.14"/>
|
||||
<template class="InAppNotification" parent="GtkRevealer">
|
||||
<property name="visible">False</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">start</property>
|
||||
<signal name="notify::child-revealed" handler="on_child_revealed" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkBox" id="layout">
|
||||
<property name="visible">True</property>
|
||||
<property name="orientation">horizontal</property>
|
||||
<property name="spacing">6</property>
|
||||
<style>
|
||||
<class name="app-notification"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkLabel" id="message_label">
|
||||
<property name="visible">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="action_button">
|
||||
<property name="visible">False</property>
|
||||
<property name="can_focus">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="close_button">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<signal name="clicked" handler="close" swapped="no"/>
|
||||
<style>
|
||||
<class name="flat"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="icon_name">window-close-symbolic</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Generated with glade 3.20.0 -->
|
||||
<!-- Generated with glade 3.22.0 -->
|
||||
<interface>
|
||||
<requires lib="gtk+" version="3.20"/>
|
||||
<template class="MainWindow" parent="GtkApplicationWindow">
|
||||
|
|
@ -11,175 +11,163 @@
|
|||
<signal name="focus-in-event" handler="on_focus_event" swapped="no"/>
|
||||
<signal name="key-release-event" handler="on_key_release_event" swapped="no"/>
|
||||
<child>
|
||||
<object class="GtkBox" id="main_layout">
|
||||
<object class="GtkOverlay" id="overlay">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkPaned" id="conversations_paned">
|
||||
<object class="GtkBox" id="main_layout">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="search_bar_box">
|
||||
<object class="GtkPaned" id="conversations_paned">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkPaned" id="folder_paned">
|
||||
<object class="GtkBox" id="search_bar_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="folder_box">
|
||||
<object class="GtkPaned" id="folder_paned">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="can_focus">True</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="folder_frame">
|
||||
<object class="GtkBox" id="folder_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="folder_list_scrolled">
|
||||
<property name="width_request">100</property>
|
||||
<object class="GtkFrame" id="folder_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="folder_list_scrolled">
|
||||
<property name="width_request">100</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
<property name="hscrollbar_policy">never</property>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
<class name="geary-folder-frame"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="geary-folder-frame"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="conversation_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="conversation_frame">
|
||||
<object class="GtkBox" id="conversation_box">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="conversation_list_scrolled">
|
||||
<property name="width_request">250</property>
|
||||
<object class="GtkFrame" id="conversation_frame">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="conversation_list_scrolled">
|
||||
<property name="width_request">250</property>
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">True</property>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
<class name="geary-conversation-frame"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="geary-conversation-frame"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">0</property>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="geary-sidebar-pane-separator"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">True</property>
|
||||
<property name="shrink">False</property>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<style>
|
||||
<class name="geary-sidebar-pane-separator"/>
|
||||
<class name="sidebar"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">False</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="info_bar_frame">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="info_bar_container">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<signal name="remove" handler="on_info_bar_container_remove" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<style>
|
||||
<class name="sidebar"/>
|
||||
<class name="geary-info-bar-frame"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="resize">False</property>
|
||||
<property name="shrink">False</property>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">True</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="pack_type">end</property>
|
||||
<property name="position">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="info_bar_frame">
|
||||
<property name="can_focus">False</property>
|
||||
<property name="no_show_all">True</property>
|
||||
<property name="label_xalign">0</property>
|
||||
<property name="shadow_type">in</property>
|
||||
<child>
|
||||
<object class="GtkGrid" id="info_bar_container">
|
||||
<property name="visible">True</property>
|
||||
<property name="can_focus">False</property>
|
||||
<property name="orientation">vertical</property>
|
||||
<signal name="remove" handler="on_info_bar_container_remove" swapped="no"/>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item">
|
||||
<placeholder/>
|
||||
</child>
|
||||
<style>
|
||||
<class name="geary-info-bar-frame"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="expand">False</property>
|
||||
<property name="fill">True</property>
|
||||
<property name="position">1</property>
|
||||
<property name="index">-1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="titlebar">
|
||||
<placeholder/>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@
|
|||
<file compressed="true" preprocess="xml-stripblanks">folder-popover.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">gtk/menus.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">in-app-notification.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">login.glade</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">main-toolbar.ui</file>
|
||||
<file compressed="true" preprocess="xml-stripblanks">main-toolbar-menus.ui</file>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue