From a4cbbf0d9250f01b27919cc3f159efe183c31d7a Mon Sep 17 00:00:00 2001
From: David Baker <dave@matrix.org>
Date: Wed, 16 Sep 2015 14:48:49 +0100
Subject: [PATCH] Backport Notifier improvements from Vector, including
 TextForEvent

---
 src/TextForEvent.js                           | 106 ++++++++++++++++++
 .../atoms/EnableNotificationsButton.js        |  58 ++++------
 src/controllers/organisms/Notifier.js         |  83 +++++++++++++-
 3 files changed, 211 insertions(+), 36 deletions(-)
 create mode 100644 src/TextForEvent.js

diff --git a/src/TextForEvent.js b/src/TextForEvent.js
new file mode 100644
index 0000000000..3d6ba2cf64
--- /dev/null
+++ b/src/TextForEvent.js
@@ -0,0 +1,106 @@
+
+function textForMemberEvent(ev) {
+    // XXX: SYJS-16
+    var senderName = ev.sender ? ev.sender.name : ev.getSender();
+    var targetName = ev.target ? ev.target.name : ev.getStateKey();
+    var reason = ev.getContent().reason ? (
+        " Reason: " + ev.getContent().reason
+    ) : "";
+    switch (ev.getContent().membership) {
+        case 'invite':
+            return senderName + " invited " + targetName + ".";
+        case 'ban':
+            return senderName + " banned " + targetName + "." + reason;
+        case 'join':
+            if (ev.getPrevContent() && ev.getPrevContent().membership == 'join') {
+                if (ev.getPrevContent().displayname && ev.getContent().displayname && ev.getPrevContent().displayname != ev.getContent().displayname) {
+                    return ev.getSender() + " changed their display name from " +
+                        ev.getPrevContent().displayname + " to " +
+                        ev.getContent().displayname;
+                } else if (!ev.getPrevContent().displayname && ev.getContent().displayname) {
+                    return ev.getSender() + " set their display name to " + ev.getContent().displayname;
+                } else if (ev.getPrevContent().displayname && !ev.getContent().displayname) {
+                    return ev.getSender() + " removed their display name";
+                } else if (ev.getPrevContent().avatar_url && !ev.getContent().avatar_url) {
+                    return ev.getSender() + " removed their profile picture";
+                } else if (ev.getPrevContent().avatar_url && ev.getContent().avatar_url && ev.getPrevContent().avatar_url != ev.getContent().avatar_url) {
+                    return ev.getSender() + " changed their profile picture";
+                } else if (!ev.getPrevContent().avatar_url && ev.getContent().avatar_url) {
+                    return ev.getSender() + " set a profile picture";
+                }
+            } else {
+                if (!ev.target) console.warn("Join message has no target! -- " + ev.getContent().state_key);
+                return targetName + " joined the room.";
+            }
+            return '';
+        case 'leave':
+            if (ev.getSender() === ev.getStateKey()) {
+                return targetName + " left the room.";
+            }
+            else if (ev.getPrevContent().membership === "ban") {
+                return senderName + " unbanned " + targetName + ".";
+            }
+            else if (ev.getPrevContent().membership === "join") {
+                return senderName + " kicked " + targetName + "." + reason;
+            }
+            else {
+                return targetName + " left the room.";
+            }
+    }
+};
+
+function textForTopicEvent(ev) {
+    var senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
+
+    return senderDisplayName + ' changed the topic to, "' + ev.getContent().topic + '"';
+};
+
+function textForMessageEvent(ev) {
+    var senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender();
+
+    var message = senderDisplayName + ': ' + ev.getContent().body;
+    if (ev.getContent().msgtype === "m.emote") {
+        message = "* " + senderDisplayName + " " + message;
+    } else if (ev.getContent().msgtype === "m.image") {
+        message = senderDisplayName + " sent an image.";
+    }
+    return message;
+};
+
+function textForCallAnswerEvent(event) {
+    var senderName = event.sender ? event.sender.name : "Someone";
+    return senderName + " answered the call.";
+};
+
+function textForCallHangupEvent(event) {
+    var senderName = event.sender ? event.sender.name : "Someone";
+    return senderName + " ended the call.";
+};
+
+function textForCallInviteEvent(event) {
+    var senderName = event.sender ? event.sender.name : "Someone";
+    // FIXME: Find a better way to determine this from the event?
+    var type = "voice";
+    if (event.getContent().offer && event.getContent().offer.sdp &&
+            event.getContent().offer.sdp.indexOf('m=video') !== -1) {
+        type = "video";
+    }
+    return senderName + " placed a " + type + " call.";
+};
+
+var handlers = {
+    'm.room.message': textForMessageEvent,
+    'm.room.topic': textForTopicEvent,
+    'm.room.member': textForMemberEvent,
+    'm.call.invite': textForCallInviteEvent,
+    'm.call.answer': textForCallAnswerEvent,
+    'm.call.hangup': textForCallHangupEvent,
+};
+
+module.exports = {
+    textForEvent: function(ev) {
+        var hdlr = handlers[ev.getType()];
+        if (!hdlr) return "";
+        return hdlr(ev);
+    }
+}
diff --git a/src/controllers/atoms/EnableNotificationsButton.js b/src/controllers/atoms/EnableNotificationsButton.js
index c600f33013..e116cd9671 100644
--- a/src/controllers/atoms/EnableNotificationsButton.js
+++ b/src/controllers/atoms/EnableNotificationsButton.js
@@ -15,53 +15,43 @@ limitations under the License.
 */
 
 'use strict';
+var sdk = require('../../index');
+var Notifier = sdk.getComponent('organisms/Notifier');
+var dis = require("../../dispatcher");
 
 module.exports = {
-    notificationsAvailable: function() {
-        return !!global.Notification;
+
+    componentDidMount: function() {
+        this.dispatcherRef = dis.register(this.onAction);
     },
 
-    havePermission: function() {
-        return global.Notification.permission == 'granted';
+    componentWillUnmount: function() {
+        dis.unregister(this.dispatcherRef);
+    },
+
+    onAction: function(payload) {
+        if (payload.action !== "notifier_enabled") {
+            return;
+        }
+        this.forceUpdate();
     },
 
     enabled: function() {
-        if (!this.havePermission()) return false;
-
-        if (!global.localStorage) return true;
-
-        var enabled = global.localStorage.getItem('notifications_enabled');
-        if (enabled === null) return true;
-        return enabled === 'true';
-    },
-
-    disable: function() {
-        if (!global.localStorage) return;
-        global.localStorage.setItem('notifications_enabled', 'false');
-        this.forceUpdate();
-    },
-
-    enable: function() {
-        if (!this.havePermission()) {
-            var that = this;
-            global.Notification.requestPermission(function() {
-                that.forceUpdate();
-            });
-        }
-
-        if (!global.localStorage) return;
-        global.localStorage.setItem('notifications_enabled', 'true');
-        this.forceUpdate();
+        return Notifier.isEnabled();
     },
 
     onClick: function() {
-        if (!this.notificationsAvailable()) {
+        var self = this;
+        if (!Notifier.supportsDesktopNotifications()) {
             return;
         }
-        if (!this.enabled()) {
-            this.enable();
+        if (!Notifier.isEnabled()) {
+            Notifier.setEnabled(true, function() {
+                self.forceUpdate();
+            });
         } else {
-            this.disable();
+            Notifier.setEnabled(false);
         }
+        this.forceUpdate();
     },
 };
diff --git a/src/controllers/organisms/Notifier.js b/src/controllers/organisms/Notifier.js
index 63e937780d..0c03222641 100644
--- a/src/controllers/organisms/Notifier.js
+++ b/src/controllers/organisms/Notifier.js
@@ -17,11 +17,21 @@ limitations under the License.
 'use strict';
 
 var MatrixClientPeg = require("../../MatrixClientPeg");
+var dis = require("../../dispatcher");
+
+/*
+ * Dispatches:
+ * {
+ *   action: "notifier_enabled",
+ *   value: boolean
+ * }
+ */
 
 module.exports = {
     start: function() {
         this.boundOnRoomTimeline = this.onRoomTimeline.bind(this);
         MatrixClientPeg.get().on('Room.timeline', this.boundOnRoomTimeline);
+        this.state = { 'toolbarHidden' : false };
     },
 
     stop: function() {
@@ -30,12 +40,81 @@ module.exports = {
         }
     },
 
+    supportsDesktopNotifications: function() {
+        return !!global.Notification;
+    },
+
+    havePermission: function() {
+        if (!this.supportsDesktopNotifications()) return false;
+        return global.Notification.permission == 'granted';
+    },
+
+    setEnabled: function(enable, callback) {
+        if(enable) {
+            if (!this.havePermission()) {
+                var self = this;
+                global.Notification.requestPermission(function() {
+                    if (callback) {
+                        callback();
+                        dis.dispatch({
+                            action: "notifier_enabled",
+                            value: true
+                        });
+                    }
+                });
+            }
+
+            if (!global.localStorage) return;
+            global.localStorage.setItem('notifications_enabled', 'true');
+
+            if (this.havePermission) {
+                dis.dispatch({
+                    action: "notifier_enabled",
+                    value: true
+                });
+            }
+        }
+        else {
+            if (!global.localStorage) return;
+            global.localStorage.setItem('notifications_enabled', 'false');
+            dis.dispatch({
+                action: "notifier_enabled",
+                value: false
+            });
+        }
+
+        this.setToolbarHidden(false);
+    },
+
+    isEnabled: function() {
+        if (!this.havePermission()) return false;
+
+        if (!global.localStorage) return true;
+
+        var enabled = global.localStorage.getItem('notifications_enabled');
+        if (enabled === null) return true;
+        return enabled === 'true';
+    },
+
+    setToolbarHidden: function(hidden) {
+        this.state.toolbarHidden = hidden;
+        dis.dispatch({
+            action: "notifier_enabled",
+            value: this.isEnabled()
+        });
+    },
+
+    isToolbarHidden: function() {
+        return this.state.toolbarHidden;
+    },
+
     onRoomTimeline: function(ev, room, toStartOfTimeline) {
         if (toStartOfTimeline) return;
         if (ev.sender && ev.sender.userId == MatrixClientPeg.get().credentials.userId) return;
 
-        var enabled = global.localStorage.getItem('notifications_enabled');
-        if (enabled === 'false') return;
+        if (!this.isEnabled()) {
+            return;
+        }
 
         var actions = MatrixClientPeg.get().getPushActionsForEvent(ev);
         if (actions && actions.notify) {