From a4cbbf0d9250f01b27919cc3f159efe183c31d7a Mon Sep 17 00:00:00 2001 From: David Baker 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) {