Ver Fonte

Move playing of sounds to the notification plugin.

Also add a config setting for the icon shown in HTML5 notificatins.

updates #443
JC Brand há 9 anos atrás
pai
commit
26cb98d963
4 ficheiros alterados com 127 adições e 114 exclusões
  1. 8 0
      docs/source/configuration.rst
  2. 4 4
      spec/chatroom.js
  3. 34 79
      src/converse-core.js
  4. 81 31
      src/converse-notification.js

+ 8 - 0
docs/source/configuration.rst

@@ -538,6 +538,14 @@ different approach.
 If you're using MAM for archiving chat room messages, you might want to set
 this option to zero.
 
+notification_icon
+-----------------
+
+* Default: ``'/logo/conversejs.png'``
+
+This option specifies which icon is shown in HTML5 notifications, as provided
+by the ``src/converse-notification.js`` plugin.
+
 
 ping_interval
 -------------

+ 4 - 4
spec/chatroom.js

@@ -196,7 +196,7 @@
                 test_utils.openChatRoom('lounge', 'localhost', 'dummy');
                 spyOn(converse, 'emit');
                 converse.play_sounds = true;
-                spyOn(converse, 'notifyOfNewMessage');
+                spyOn(converse, 'playSoundNotification');
                 var view = this.chatboxviews.get('lounge@localhost');
                 if (!view.$el.find('.chat-area').length) { view.renderChatArea(); }
                 var text = 'This message will play a sound because it mentions dummy';
@@ -207,7 +207,7 @@
                     type: 'groupchat'
                 }).c('body').t(text);
                 view.onChatRoomMessage(message.nodeTree);
-                expect(converse.notifyOfNewMessage).toHaveBeenCalled();
+                expect(converse.playSoundNotification).toHaveBeenCalled();
 
                 text = "This message won't play a sound";
                 message = $msg({
@@ -217,7 +217,7 @@
                     type: 'groupchat'
                 }).c('body').t(text);
                 view.onChatRoomMessage(message.nodeTree);
-                expect(converse.notifyOfNewMessage, 1);
+                expect(converse.playSoundNotification, 1);
                 converse.play_sounds = false;
 
                 text = "This message won't play a sound because it is sent by dummy";
@@ -228,7 +228,7 @@
                     type: 'groupchat'
                 }).c('body').t(text);
                 view.onChatRoomMessage(message.nodeTree);
-                expect(converse.notifyOfNewMessage, 1);
+                expect(converse.playSoundNotification, 1);
                 converse.play_sounds = false;
             }.bind(converse));
 

+ 34 - 79
src/converse-core.js

@@ -106,14 +106,6 @@
         9: 'REDIRECT'
     };
 
-    // XEP-0085 Chat states
-    // http://xmpp.org/extensions/xep-0085.html
-    var INACTIVE = 'inactive';
-    var ACTIVE = 'active';
-    var COMPOSING = 'composing';
-    var PAUSED = 'paused';
-    var GONE = 'gone';
-
     // TODO Refactor into external MAM plugin
     // XEP-0059 Result Set Management
     var RSM_ATTRIBUTES = ['max', 'first', 'last', 'after', 'before', 'index', 'count'];
@@ -201,20 +193,6 @@
     };
 
 
-    converse.isOnlyChatStateNotification = function ($msg) {
-        // See XEP-0085 Chat State Notification
-        return (
-            $msg.find('body').length === 0 && (
-                $msg.find(ACTIVE).length !== 0 ||
-                $msg.find(COMPOSING).length !== 0 ||
-                $msg.find(INACTIVE).length !== 0 ||
-                $msg.find(PAUSED).length !== 0 ||
-                $msg.find(GONE).length !== 0
-            )
-        );
-    };
-
-
     converse.log = function (txt, level) {
         var logger;
         if (typeof console === "undefined" || typeof console.log === "undefined") {
@@ -261,11 +239,21 @@
         Strophe.addNamespace('RSM', 'http://jabber.org/protocol/rsm');
         Strophe.addNamespace('XFORM', 'jabber:x:data');
 
+        // Instance level constants
         this.TIMEOUTS = { // Set as module attr so that we can override in tests.
             'PAUSED':     20000,
             'INACTIVE':   90000
         };
 
+        // XEP-0085 Chat states
+        // http://xmpp.org/extensions/xep-0085.html
+        this.INACTIVE = 'inactive';
+        this.ACTIVE = 'active';
+        this.COMPOSING = 'composing';
+        this.PAUSED = 'paused';
+        this.GONE = 'gone';
+
+
         // Detect support for the user's locale
         // ------------------------------------
         this.isConverseLocale = function (locale) { return typeof locales[locale] !== "undefined"; };
@@ -378,7 +366,6 @@
             message_carbons: false, // Support for XEP-280
             no_trimming: false, // Set to true for phantomjs tests (where browser apparently has no width)
             password: undefined,
-            play_sounds: false,
             prebind: false, // XXX: Deprecated, use "authentication" instead.
             prebind_url: null,
             rid: undefined,
@@ -386,7 +373,6 @@
             show_only_online_users: false,
             show_toolbar: true,
             sid: undefined,
-            sounds_path: '/sounds/',
             storage: 'session',
             synchronize_availability: true, // Set to false to not sync with other clients or with resource name of the particular client that it should synchronize with
             use_vcards: true,
@@ -453,7 +439,7 @@
             /* Send out a Chat Status Notification (XEP-0352) */
             if (converse.features[Strophe.NS.CSI] || true) {
                 converse.connection.send($build(stat, {xmlns: Strophe.NS.CSI}));
-                this.inactive = (stat === INACTIVE) ? true : false;
+                this.inactive = (stat === converse.INACTIVE) ? true : false;
             }
         };
 
@@ -468,7 +454,7 @@
                 return;
             }
             if (this.inactive) {
-                this.sendCSI(ACTIVE);
+                this.sendCSI(converse.ACTIVE);
             }
             if (this.auto_changed_status === true) {
                 this.auto_changed_status = false;
@@ -489,7 +475,7 @@
             var stat = this.xmppstatus.getStatus();
             this.idle_seconds++;
             if (this.csi_waiting_time > 0 && this.idle_seconds > this.csi_waiting_time && !this.inactive) {
-                this.sendCSI(INACTIVE);
+                this.sendCSI(converse.INACTIVE);
             }
             if (this.auto_away > 0 && this.idle_seconds > this.auto_away && stat !== 'away' && stat !== 'xa') {
                 this.auto_changed_status = true;
@@ -514,36 +500,6 @@
             window.setInterval(this.onEverySecond.bind(this), 1000);
         };
 
-        this.shouldNotifyOfNewMessage = function ($message) {
-            var $forwarded = $message.find('forwarded');
-            if ($forwarded.length) {
-                return false;
-            }
-            var is_me = Strophe.getBareJidFromJid($message.attr('from')) === converse.bare_jid;
-            return !converse.isOnlyChatStateNotification($message) && !is_me;
-        };
-
-        this.notifyOfNewMessage = function ($message) {
-            /* Plays a sound to notify that a new message was recieved.
-             *
-             * Returns true if the notification was made and false otherwise.
-             */
-            if (!this.shouldNotifyOfNewMessage($message)) {
-                return false;
-            }
-            var audio;
-            if (converse.play_sounds && typeof Audio !== "undefined") {
-                audio = new Audio(converse.sounds_path+"msg_received.ogg");
-                if (audio.canPlayType('/audio/ogg')) {
-                    audio.play();
-                } else {
-                    audio = new Audio(converse.sounds_path+"msg_received.mp3");
-                    audio.play();
-                }
-            }
-            return true;
-        };
-
         this.giveFeedback = function (message, klass) {
             $('.conn-feedback').each(function (idx, el) {
                 var $el = $(el);
@@ -1413,11 +1369,11 @@
                     fullname = this.get('fullname'),
                     is_groupchat = $message.attr('type') === 'groupchat',
                     msgid = $message.attr('id'),
-                    chat_state = $message.find(COMPOSING).length && COMPOSING ||
-                        $message.find(PAUSED).length && PAUSED ||
-                        $message.find(INACTIVE).length && INACTIVE ||
-                        $message.find(ACTIVE).length && ACTIVE ||
-                        $message.find(GONE).length && GONE,
+                    chat_state = $message.find(converse.COMPOSING).length && converse.COMPOSING ||
+                        $message.find(converse.PAUSED).length && converse.PAUSED ||
+                        $message.find(converse.INACTIVE).length && converse.INACTIVE ||
+                        $message.find(converse.ACTIVE).length && converse.ACTIVE ||
+                        $message.find(converse.GONE).length && converse.GONE,
                     stamp, time, sender, from;
 
                 if (is_groupchat) {
@@ -1830,14 +1786,14 @@
             },
 
             handleChatStateMessage: function (message) {
-                if (message.get('chat_state') === COMPOSING) {
+                if (message.get('chat_state') === converse.COMPOSING) {
                     this.showStatusNotification(message.get('fullname')+' '+__('is typing'));
                     this.clear_status_timeout = window.setTimeout(this.clearStatusNotification.bind(this), 10000);
-                } else if (message.get('chat_state') === PAUSED) {
+                } else if (message.get('chat_state') === converse.PAUSED) {
                     this.showStatusNotification(message.get('fullname')+' '+__('has stopped typing'));
-                } else if (_.contains([INACTIVE, ACTIVE], message.get('chat_state'))) {
+                } else if (_.contains([converse.INACTIVE, converse.ACTIVE], message.get('chat_state'))) {
                     this.$content.find('div.chat-event').remove();
-                } else if (message.get('chat_state') === GONE) {
+                } else if (message.get('chat_state') === converse.GONE) {
                     this.showStatusNotification(message.get('fullname')+' '+__('has gone away'));
                 }
             },
@@ -1876,7 +1832,7 @@
                             type: 'chat',
                             id: message.get('msgid')
                        }).c('body').t(message.get('message')).up()
-                         .c(ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).up();
+                         .c(converse.ACTIVE, {'xmlns': Strophe.NS.CHATSTATES}).up();
             },
 
             sendMessage: function (message) {
@@ -1967,12 +1923,12 @@
                     window.clearTimeout(this.chat_state_timeout);
                     delete this.chat_state_timeout;
                 }
-                if (state === COMPOSING) {
+                if (state === converse.COMPOSING) {
                     this.chat_state_timeout = window.setTimeout(
-                            this.setChatState.bind(this), converse.TIMEOUTS.PAUSED, PAUSED);
-                } else if (state === PAUSED) {
+                            this.setChatState.bind(this), converse.TIMEOUTS.PAUSED, converse.PAUSED);
+                } else if (state === converse.PAUSED) {
                     this.chat_state_timeout = window.setTimeout(
-                            this.setChatState.bind(this), converse.TIMEOUTS.INACTIVE, INACTIVE);
+                            this.setChatState.bind(this), converse.TIMEOUTS.INACTIVE, converse.INACTIVE);
                 }
                 if (!no_save && this.model.get('chat_state') !== state) {
                     this.model.set('chat_state', state);
@@ -1996,11 +1952,11 @@
                         }
                         converse.emit('messageSend', message);
                     }
-                    this.setChatState(ACTIVE);
+                    this.setChatState(converse.ACTIVE);
                 } else if (!this.model.get('chatroom')) { // chat state data is currently only for single user chat
                     // Set chat state to composing if keyCode is not a forward-slash
                     // (which would imply an internal command and not a message).
-                    this.setChatState(COMPOSING, ev.keyCode === KEY.FORWARD_SLASH);
+                    this.setChatState(converse.COMPOSING, ev.keyCode === KEY.FORWARD_SLASH);
                 }
             },
 
@@ -2155,7 +2111,7 @@
                 if (ev && ev.preventDefault) { ev.preventDefault(); }
                 if (converse.connection.connected) {
                     this.model.destroy();
-                    this.setChatState(INACTIVE);
+                    this.setChatState(converse.INACTIVE);
                 } else {
                     this.hide();
                 }
@@ -2167,7 +2123,7 @@
                 converse.chatboxviews.trimChats(this);
                 utils.refreshWebkit();
                 this.$content.scrollTop(this.model.get('scroll'));
-                this.setChatState(ACTIVE).focus();
+                this.setChatState(converse.ACTIVE).focus();
                 converse.emit('chatBoxMaximized', this);
             },
 
@@ -2183,7 +2139,7 @@
                 // save the scroll position to restore it on maximize
                 this.model.save({'scroll': this.$content.scrollTop()});
                 // Minimizes a chat box
-                this.setChatState(INACTIVE).model.minimize();
+                this.setChatState(converse.INACTIVE).model.minimize();
                 this.$el.hide('fast', utils.refreshwebkit);
                 converse.emit('chatBoxMinimized', this);
             },
@@ -2282,7 +2238,7 @@
                         // localstorage
                         this.model.save();
                     }
-                    this.setChatState(ACTIVE);
+                    this.setChatState(converse.ACTIVE);
                     this.scrollDown();
                     if (focus) {
                         this.focus();
@@ -2389,7 +2345,6 @@
                 if (msgid && chatbox.messages.findWhere({msgid: msgid})) {
                     return true; // We already have this message stored.
                 }
-                converse.notifyOfNewMessage($message);
                 chatbox.createMessage($message, $delay, archive_id);
                 converse.roster.addResource(contact_jid, resource);
                 converse.emit('message', message);
@@ -3160,7 +3115,7 @@
                     return;
                 }
                 plugin.converse = converse;
-                _.each(Object.keys(plugin.overrides), function (key) {
+                _.each(Object.keys(plugin.overrides || {}), function (key) {
                     /* We automatically override all methods and Backbone views and
                      * models that are in the "overrides" namespace.
                      */

+ 81 - 31
src/converse-notification.js

@@ -10,46 +10,77 @@
     define("converse-notification", ["converse-core", "converse-api"], factory);
 }(this, function (converse, converse_api) {
     "use strict";
-    var utils = converse_api.env.utils;
-    var Strophe = converse_api.env.Strophe;
+    var $ = converse_api.env.jQuery,
+        utils = converse_api.env.utils,
+        Strophe = converse_api.env.Strophe,
+        _ = converse_api.env._;
     // For translations
     var __ = utils.__.bind(converse);
     var ___ = utils.___;
 
-    if (!("Notification" in window)) {
-        // HTML5 notifications aren't supported.
-        converse.log(
-            "Not loading the notifications plugin because this browser "+
-            "doesn't support HTML5 notifications.");
-        return;
-    }
-    // Ask user to enable HTML5 notifications
-    Notification.requestPermission();
+    var supports_html5_notification = "Notification" in window;
 
+    if (supports_html5_notification && Notification.permission !== 'denied') {
+        // Ask user to enable HTML5 notifications
+        Notification.requestPermission();
+    }
 
     converse_api.plugins.add('notification', {
 
-        overrides: {
-            // Overrides mentioned here will be picked up by converse.js's
-            // plugin architecture they will replace existing methods on the
-            // relevant objects or classes.
-            //
-            // New functions which don't exist yet can also be added.
-
-            notifyOfNewMessage: function ($message) {
-                var result = this._super.notifyOfNewMessage.apply(this, arguments);
-                if (result && (this.windowState === 'blur') && (Notification.permission === "granted")) {
-                    this.showMessageNotification($message);
-                }
-                return result;
-            }
-        },
-
         initialize: function () {
             /* The initialize function gets called as soon as the plugin is
              * loaded by converse.js's plugin machinery.
              */
             var converse = this.converse;
+            // Configuration values for this plugin
+            var settings = {
+                play_sounds: false,
+                sounds_path: '/sounds/',
+                notification_icon: '/logo/conversejs.png'
+            };
+            _.extend(converse.default_settings, settings);
+            _.extend(converse, settings);
+            _.extend(converse, _.pick(converse.user_settings, Object.keys(settings)));
+
+            converse.isOnlyChatStateNotification = function ($msg) {
+                // See XEP-0085 Chat State Notification
+                return (
+                    $msg.find('body').length === 0 && (
+                        $msg.find(converse.ACTIVE).length !== 0 ||
+                        $msg.find(converse.COMPOSING).length !== 0 ||
+                        $msg.find(converse.INACTIVE).length !== 0 ||
+                        $msg.find(converse.PAUSED).length !== 0 ||
+                        $msg.find(converse.GONE).length !== 0
+                    )
+                );
+            };
+
+            converse.shouldNotifyOfNewMessage = function ($message) {
+                var $forwarded = $message.find('forwarded');
+                if ($forwarded.length) {
+                    return false;
+                }
+                var is_me = Strophe.getBareJidFromJid($message.attr('from')) === converse.bare_jid;
+                return !converse.isOnlyChatStateNotification($message) && !is_me;
+            };
+
+            converse.playSoundNotification = function ($message) {
+                /* Plays a sound to notify that a new message was recieved.
+                 */
+                // XXX Eventually this can be refactored to use Notification's sound
+                // feature, but no browser currently supports it.
+                // https://developer.mozilla.org/en-US/docs/Web/API/notification/sound
+                var audio;
+                if (converse.play_sounds && typeof Audio !== "undefined") {
+                    audio = new Audio(converse.sounds_path+"msg_received.ogg");
+                    if (audio.canPlayType('/audio/ogg')) {
+                        audio.play();
+                    } else {
+                        audio = new Audio(converse.sounds_path+"msg_received.mp3");
+                        audio.play();
+                    }
+                }
+            };
 
             converse.showChatStateNotification = function (event, contact) {
                 /* Show an HTML5 notification indicating that a contact changed
@@ -77,20 +108,39 @@
                 setTimeout(n.close.bind(n), 5000);
             };
 
-            converse.on('contactStatusChanged',  converse.showChatStateNotification);
-
             converse.showMessageNotification = function ($message) {
-                /* Show an HTML5 notification of a received message.
+                /* Shows an HTML5 Notification to indicate that a new chat
+                 * message was received.
                  */
+                if (!supports_html5_notification ||
+                        this.windowState !== 'blur' ||
+                        Notification.permission !== "granted") {
+                    return;
+                }
                 var contact_jid = Strophe.getBareJidFromJid($message.attr('from'));
                 var roster_item = converse.roster.get(contact_jid);
                 var n = new Notification(__(___("%1$s says"), roster_item.get('fullname')), {
                         body: $message.children('body').text(),
                         lang: converse.i18n.locale_data.converse[""].lang,
-                        icon: 'logo/conversejs.png'
+                        icon: converse.notification_icon
                     });
                 setTimeout(n.close.bind(n), 5000);
             };
+
+            converse.notifyOfNewMessage = function (message) {
+                /* Event handler for the on('message') event. Will call methods
+                 * to play sounds and show HTML5 notifications.
+                 */
+                var $message = $(message);
+                if (!converse.shouldNotifyOfNewMessage($message)) {
+                    return false;
+                }
+                converse.playSoundNotification($message);
+                converse.showMessageNotification($message);
+            };
+
+            converse.on('contactStatusChanged',  converse.showChatStateNotification);
+            converse.on('message',  converse.showMessageNotification);
         }
     });
 }));