diff --git a/po/POTFILES.in b/po/POTFILES.in index b36608be..2d0d908d 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3c4d50bf..12fba45f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala index 33b4eca4..159dd5af 100644 --- a/src/client/application/geary-controller.vala +++ b/src/client/application/geary-controller.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"); } diff --git a/src/client/components/main-window.vala b/src/client/components/main-window.vala index d7fe781a..7e5ab0ef 100644 --- a/src/client/components/main-window.vala +++ b/src/client/components/main-window.vala @@ -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(), diff --git a/src/client/meson.build b/src/client/meson.build index d7123ab5..b0559b8d 100644 --- a/src/client/meson.build +++ b/src/client/meson.build @@ -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', diff --git a/src/client/notification/in-app-notification.vala b/src/client/notification/in-app-notification.vala new file mode 100644 index 00000000..88993092 --- /dev/null +++ b/src/client/notification/in-app-notification.vala @@ -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(); + } +} diff --git a/src/client/util/util-email.vala b/src/client/util/util-email.vala index 40d16928..143465e5 100644 --- a/src/client/util/util-email.vala +++ b/src/client/util/util-email.vala @@ -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); + } + +} diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 4513df64..4449d945 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -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" diff --git a/ui/in-app-notification.ui b/ui/in-app-notification.ui new file mode 100644 index 00000000..087d794b --- /dev/null +++ b/ui/in-app-notification.ui @@ -0,0 +1,48 @@ + + + + + diff --git a/ui/main-window.ui b/ui/main-window.ui index e09e308a..ae2aa252 100644 --- a/ui/main-window.ui +++ b/ui/main-window.ui @@ -1,5 +1,5 @@ - + diff --git a/ui/org.gnome.Geary.gresource.xml b/ui/org.gnome.Geary.gresource.xml index d545a9af..e69af90b 100644 --- a/ui/org.gnome.Geary.gresource.xml +++ b/ui/org.gnome.Geary.gresource.xml @@ -27,6 +27,7 @@ folder-popover.ui gtk/help-overlay.ui gtk/menus.ui + in-app-notification.ui login.glade main-toolbar.ui main-toolbar-menus.ui