[gnome-shell/wip/fmuellner/notification-redux+sass: 108/141] calendar: Add NotificationSection to message list



commit c97812b6804abb221b90ec42ca1b8e7465b06a7a
Author: Florian Müllner <fmuellner gnome org>
Date:   Wed Feb 11 20:41:56 2015 +0100

    calendar: Add NotificationSection to message list
    
    Display notifications that have not been dismissed in the message
    list - eventually this will replace the existing message tray summary.
    Notification messages show icon, title and one line of the body and
    can be clicked to activate the default action. However they cannot be
    expanded, so other actions or the full body text are not accessible
    in this mode.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=744817

 data/gnome-shell-theme.gresource.xml |    1 +
 data/theme/no-notifications.svg      |  114 +++++++++++++++++++
 js/ui/calendar.js                    |  198 +++++++++++++++++++++++++++++++++-
 js/ui/main.js                        |    2 +-
 4 files changed, 310 insertions(+), 5 deletions(-)
---
diff --git a/data/gnome-shell-theme.gresource.xml b/data/gnome-shell-theme.gresource.xml
index 9e270d0..f145325 100644
--- a/data/gnome-shell-theme.gresource.xml
+++ b/data/gnome-shell-theme.gresource.xml
@@ -20,6 +20,7 @@
     <file>logged-in-indicator.svg</file>
     <file>more-results.svg</file>
     <file>no-events.svg</file>
+    <file>no-notifications.svg</file>
     <file>noise-texture.png</file>
     <file>page-indicator-active.svg</file>
     <file>page-indicator-inactive.svg</file>
diff --git a/data/theme/no-notifications.svg b/data/theme/no-notifications.svg
new file mode 100644
index 0000000..23af556
--- /dev/null
+++ b/data/theme/no-notifications.svg
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://creativecommons.org/ns#";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="64px"
+   height="64px"
+   id="svg3393"
+   version="1.1"
+   inkscape:version="0.48.5 r10040"
+   sodipodi:docname="New document 2">
+  <defs
+     id="defs3395" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="5.5"
+     inkscape:cx="32"
+     inkscape:cy="32"
+     inkscape:current-layer="layer1"
+     showgrid="true"
+     inkscape:document-units="px"
+     inkscape:grid-bbox="true"
+     inkscape:window-width="697"
+     inkscape:window-height="613"
+     inkscape:window-x="100"
+     inkscape:window-y="77"
+     inkscape:window-maximized="0" />
+  <metadata
+     id="metadata3398">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     id="layer1"
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer">
+    <g
+       style="display:inline"
+       transform="matrix(4,0,0,4,0.29733827,-0.35415646)"
+       id="g19245">
+      <g
+         id="g19247"
+         inkscape:label="status"
+         style="display:inline"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19249"
+         inkscape:label="devices"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19251"
+         inkscape:label="apps"
+         transform="translate(-323.02908,-649.02581)">
+        <path
+           inkscape:connector-curvature="0"
+           d="m 331.9377,653 c 0.0187,0.16677 0.0625,0.32822 0.0625,0.5 0,2.48528 -2.01472,4.5 -4.5,4.5 
-0.11769,0 -0.22834,-0.0224 -0.34375,-0.0312 l 0,2.21875 c 0,1.00412 0.80838,1.8125 1.8125,1.8125 l 
1.54511,-5e-5 2,2.04688 2.0625,-2.04688 1.61114,0 c 1.00413,0 1.8125,-0.80838 1.8125,-1.8125 l 0,-5.375 c 
0,-1.00412 -0.80837,-1.8125 -1.8125,-1.8125 z"
+           id="path19253"
+           sodipodi:nodetypes="csscsscccssssc"
+           
style="opacity:0.5;color:#000000;fill:#c3c3c3;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 />
+        <path
+           inkscape:connector-curvature="0"
+           d="m 327.5002,650 c -1.933,0 -3.5,1.567 -3.5,3.5 0,1.933 1.567,3.5 3.5,3.5 1.933,0 3.5,-1.567 
3.5,-3.5 0,-1.933 -1.567,-3.5 -3.5,-3.5 z m -0.53125,1 1.03125,0 -0.0625,1.375 a 0.19951718,0.19951718 0 0 0 
0,0.0625 0.19951718,0.19951718 0 0 0 0,0.0312 0.19951718,0.19951718 0 0 0 0.125,0.125 0.19951718,0.19951718 0 
0 0 0.0312,0 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 
0 0 0 0.0312,-0.0312 l 1.15625,-0.75 0.5,0.90625 -1.21875,0.625 a 0.19951718,0.19951718 0 0 0 -0.0312,0 
0.19951718,0.19951718 0 0 0 -0.0312,0.0312 0.19951718,0.19951718 0 0 0 -0.0312,0.0937 0.19951718,0.19951718 0 
0 0 0,0.0625 0.19951718,0.19951718 0 0 0 0,0.0312 0.19951718,0.19951718 0 0 0 0.0312,0.0625 
0.19951718,0.19951718 0 0 0 0.0312,0.0312 0.19951718,0.19951718 0 0 0 0.0312,0.0312 l 1.25,0.625 
-0.53125,0.90625 -1.15625,-0.781 a 0.19951718,0.19951718 0 0 0 -0.0312,0 0.19951718,0.19951718 0 0 0 
-0.0625,-0.0312 0.19951718,0.19951718 0 0 0 -0.0625,
 0 0.19951718,0.19951718 0 0 0 -0.125,0.0937 0.19951718,0.19951718 0 0 0 -0.0312,0.0312 0.19951718,0.19951718 
0 0 0 0,0.0312 0.19951718,0.19951718 0 0 0 0,0.0625 l 0.0625,1.3751 -1.03125,0 0.0937,-1.375 a 
0.19951718,0.19951718 0 0 0 -0.0312,-0.0937 0.19951718,0.19951718 0 0 0 -0.0312,-0.0625 0.19951718,0.19951718 
0 0 0 -0.0625,-0.0312 0.19951718,0.19951718 0 0 0 -0.0625,-0.0312 0.19951718,0.19951718 0 0 0 -0.0312,0 
0.19951718,0.19951718 0 0 0 -0.0937,0.0312 l -1.1875,0.78125 -0.5,-0.90625 1.25,-0.625 a 
0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 
0 0 0.0312,-0.0625 0.19951718,0.19951718 0 0 0 0,-0.0312 0.19951718,0.19951718 0 0 0 0,-0.0625 
0.19951718,0.19951718 0 0 0 0,-0.0312 0.19951718,0.19951718 0 0 0 -0.0312,-0.0625 0.19951718,0.19951718 0 0 0 
-0.0312,-0.0312 0.19951718,0.19951718 0 0 0 -0.0312,0 l -1.25,-0.625 0.5,-0.90625 1.1875,0.75 a 
0.19951718,0.19951718 0 0 0 0.0312,0.0312 0.19951718,0.19951718 0 0 0 0
 .0625,0 0.19951718,0.19951718 0 0 0 0.0625,0 0.19951718,0.19951718 0 0 0 0.0312,0 0.19951718,0.19951718 0 0 
0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0312 
0.19951718,0.19951718 0 0 0 0,-0.0312 0.19951718,0.19951718 0 0 0 0.0312,-0.0625 0.19951718,0.19951718 0 0 0 
0,-0.0312 L 326.96895,651 z"
+           id="path19255"
+           
style="color:#000000;fill:#bebebe;fill-opacity:1;fill-rule:nonzero;stroke:none;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate"
 />
+      </g>
+      <g
+         id="g19257"
+         inkscape:label="places"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19259"
+         inkscape:label="mimetypes"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19261"
+         inkscape:label="emblems"
+         style="display:inline"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19263"
+         inkscape:label="emotes"
+         style="display:inline"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19265"
+         inkscape:label="categories"
+         style="display:inline"
+         transform="translate(-323.02908,-649.02581)" />
+      <g
+         id="g19267"
+         inkscape:label="actions"
+         style="display:inline"
+         transform="translate(-323.02908,-649.02581)" />
+    </g>
+  </g>
+</svg>
diff --git a/js/ui/calendar.js b/js/ui/calendar.js
index 8204315..ecb609b 100644
--- a/js/ui/calendar.js
+++ b/js/ui/calendar.js
@@ -15,6 +15,7 @@ const Meta = imports.gi.Meta;
 const Shell = imports.gi.Shell;
 
 const Main = imports.ui.main;
+const MessageTray = imports.ui.messageTray;
 const Tweener = imports.ui.tweener;
 const Util = imports.misc.util;
 
@@ -974,6 +975,61 @@ const Message = new Lang.Class({
 });
 Signals.addSignalMethods(Message.prototype);
 
+const NotificationMessage = new Lang.Class({
+    Name: 'NotificationMessage',
+    Extends: Message,
+
+    _init: function(notification) {
+        this.notification = notification;
+
+        this.setUseBodyMarkup(notification.bannerBodyMarkup);
+        this.parent(notification.title, notification.bannerBodyText);
+
+        this.setIcon(this._getIcon());
+
+        this.connect('close', Lang.bind(this,
+            function() {
+                this._closed = true;
+                this.notification.destroy(MessageTray.NotificationDestroyedReason.DISMISSED);
+            }));
+        notification.connect('destroy', Lang.bind(this,
+            function() {
+                if (!this._closed)
+                    this.emit('close');
+            }));
+        this._updatedId = notification.connect('updated',
+                                               Lang.bind(this, this._onUpdated));
+    },
+
+    _getIcon: function() {
+        if (this.notification.gicon)
+            return new St.Icon({ gicon: this.notification.gicon, icon_size: 48 });
+        else
+            return this.notification.source.createIcon(48);
+    },
+
+    _onUpdated: function(n, clear) {
+        this.setIcon(this._getIcon());
+        this.setTitle(n.title);
+        this.setBody(n.bannerBodyText);
+        this.setUseBodyMarkup(n.bannerBodyMarkup);
+    },
+
+    canClear: function() {
+        return !this.notification.resident;
+    },
+
+    _onClicked: function() {
+        this.notification.activate();
+    },
+
+    _onDestroy: function() {
+        if (this._updatedId)
+            this.notification.disconnect(this._updatedId);
+        this._updatedId = 0;
+    }
+});
+
 const MessageListSection = new Lang.Class({
     Name: 'MessageListSection',
 
@@ -1275,6 +1331,109 @@ const EventsSection = new Lang.Class({
     }
 });
 
+const NotificationSection = new Lang.Class({
+    Name: 'NotificationSection',
+    Extends: MessageListSection,
+
+    _init: function() {
+        this.parent('Notifications');
+
+        this._sources = new Map();
+        this._nUrgent = 0;
+
+        Main.messageTray.connect('source-added', Lang.bind(this, this._sourceAdded));
+        Main.messageTray.getSources().forEach(Lang.bind(this, function(source) {
+            this._sourceAdded(Main.messageTray, source);
+        }));
+
+        this.actor.connect('notify::mapped', Lang.bind(this, this._onMapped));
+
+        Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
+        this._sessionUpdated();
+    },
+
+    _sourceAdded: function(tray, source) {
+        let obj = {
+            destroyId: 0,
+            notificationAddedId: 0,
+        };
+
+        obj.destroyId = source.connect('destroy', Lang.bind(this, function(source) {
+            this._onSourceDestroy(source, obj);
+        }));
+        obj.notificationAddedId = source.connect('notification-added',
+                                                 Lang.bind(this, this._onNotificationAdded));
+
+        this._sources.set(source, obj);
+    },
+
+    _onNotificationAdded: function(source, notification) {
+        let message = new NotificationMessage(notification);
+
+        let time = new Date().toLocaleFormat(C_("event list time", "%H\u2236%M"));
+        message.setSecondaryActor(new St.Label({ style_class: 'event-time',
+                                                 x_align: Clutter.ActorAlign.END,
+                                                 text: time }));
+
+        let isUrgent = notification.urgency == MessageTray.Urgency.CRITICAL;
+        if (isUrgent) {
+            // Keep track of urgent notifications to keep them on top
+            this._nUrgent++;
+
+            let id = notification.connect('destroy', Lang.bind(this,
+                function() {
+                    notification.disconnect(id);
+                    this._nUrgent--;
+                }));
+        } else if (this.mapped) {
+            // Only acknowledge non-urgent notifications in case it
+            // has important actions that are inaccessible when not
+            // shown as banner
+            notification.acknowledged = true;
+        }
+
+        let index = isUrgent ? 0 : this._nUrgent;
+        this.addMessageAtIndex(message, index, this.actor.mapped);
+    },
+
+    _onSourceDestroy: function(source, obj) {
+        source.disconnect(obj.destroyId);
+        source.disconnect(obj.notificationAddedId);
+
+        this._sources.delete(source);
+    },
+
+    _onMapped: function() {
+        if (!this.actor.mapped)
+            return;
+
+        for (let message of this._messages.keys())
+            if (message.notification.urgency != MessageTray.Urgency.CRITICAL)
+                message.notification.acknowledged = true;
+    },
+
+    _onTitleClicked: function() {
+        this.parent();
+
+        let app = Shell.AppSystem.get_default().lookup_app('gnome-notifications-panel.desktop');
+
+        if (!app) {
+            log('Settings panel for desktop file ' + desktopFile + ' could not be loaded!');
+            return;
+        }
+
+        app.activate();
+    },
+
+    _syncVisible: function() {
+        this.actor.visible = !this.empty && this._isToday();
+    },
+
+    _sessionUpdated: function() {
+        this._title.reactive = Main.sessionMode.allowSettings;
+    }
+});
+
 const Placeholder = new Lang.Class({
     Name: 'Placeholder',
 
@@ -1284,14 +1443,41 @@ const Placeholder = new Lang.Class({
 
         this._date = new Date();
 
-        let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-events.svg');
-        let gicon = new Gio.FileIcon({ file: file });
+        let todayFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-notifications.svg');
+        let otherFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/no-events.svg');
+        this._todayIcon = new Gio.FileIcon({ file: todayFile });
+        this._otherIcon = new Gio.FileIcon({ file: otherFile });
 
-        this._icon = new St.Icon({ gicon: gicon });
+        this._icon = new St.Icon();
         this.actor.add_actor(this._icon);
 
-        this._label = new St.Label({ text: _("No Events") });
+        this._label = new St.Label();
         this.actor.add_actor(this._label);
+
+        this._sync();
+    },
+
+    setDate: function(date) {
+        if (_sameDay(this._date, date))
+            return;
+        this._date = date;
+        this._sync();
+    },
+
+    _sync: function() {
+        let isToday = _sameDay(this._date, new Date());
+        if (isToday && this._icon.gicon == this._todayIcon)
+            return;
+        if (!isToday && this._icon.gicon == this._otherIcon)
+            return;
+
+        if (isToday) {
+            this._icon.gicon = this._todayIcon;
+            this._label.text = _("No Notifications");
+        } else {
+            this._icon.gicon = this._otherIcon;
+            this._label.text = _("No Events");
+        }
     }
 });
 
@@ -1320,6 +1506,9 @@ const MessageList = new Lang.Class({
         this._scrollView.add_actor(this._sectionList);
         this._sections = new Map();
 
+        this._notificationSection = new NotificationSection();
+        this._addSection(this._notificationSection);
+
         this._eventsSection = new EventsSection();
         this._addSection(this._eventsSection);
     },
@@ -1377,5 +1566,6 @@ const MessageList = new Lang.Class({
     setDate: function(date) {
         for (let section of this._sections.keys())
             section.setDate(date);
+        this._placeholder.setDate(date);
     }
 });
diff --git a/js/ui/main.js b/js/ui/main.js
index 2fb674c..eb65458 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -159,8 +159,8 @@ function _initializeUI() {
     if (LoginManager.canLock())
         screenShield = new ScreenShield.ScreenShield();
 
-    panel = new Panel.Panel();
     messageTray = new MessageTray.MessageTray();
+    panel = new Panel.Panel();
     keyboard = new Keyboard.Keyboard();
     notificationDaemon = new NotificationDaemon.NotificationDaemon();
     windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]