From 99d8d2bfb7be6501e46b60ba060e946518388545 Mon Sep 17 00:00:00 2001 From: Nick Fisher Date: Wed, 17 Dec 2014 22:43:57 +0100 Subject: [PATCH] Abstract desktop notifications to a class --- client/src/applets/settings.js | 21 ++-- client/src/helpers/desktopnotifications.js | 126 +++++++++++++++++++++ client/src/views/application.js | 28 +---- 3 files changed, 143 insertions(+), 32 deletions(-) create mode 100644 client/src/helpers/desktopnotifications.js diff --git a/client/src/applets/settings.js b/client/src/applets/settings.js index b86d3d2..504fe60 100644 --- a/client/src/applets/settings.js +++ b/client/src/applets/settings.js @@ -4,7 +4,7 @@ 'change [data-setting]': 'saveSettings', 'click [data-setting="theme"]': 'selectTheme', 'click .register_protocol': 'registerProtocol', - 'click .enable_notifications': 'enableNoticiations' + 'click .enable_notifications': 'enableNotifications' }, initialize: function (options) { @@ -36,8 +36,9 @@ this.$el.find('.protocol_handler').remove(); } - if (!window.webkitNotifications) { - this.$el.find('notification_enabler').remove(); + + if (_kiwi.global.utils.notifications.allowed() !== null) { + this.$el.find('.notification_enabler').remove(); } // Incase any settings change while we have this open, update them @@ -124,14 +125,14 @@ navigator.registerProtocolHandler('ircs', document.location.origin + _kiwi.app.get('base_path') + '/%s', 'Kiwi IRC'); }, - enableNoticiations: function(event){ + enableNotifications: function(event){ event.preventDefault(); - - if ('webkitNotifications' in window) { - window.webkitNotifications.requestPermission(); - } else if ('Notification' in window) { - Notification.requestPermission(); - } + var notifications = _kiwi.global.utils.notifications; + notifications.requestPermission().always(_.bind(function () { + if (notifications.allowed() !== null) { + this.$('.notification_enabler').remove(); + } + }, this)); } }); diff --git a/client/src/helpers/desktopnotifications.js b/client/src/helpers/desktopnotifications.js new file mode 100644 index 0000000..bf1de23 --- /dev/null +++ b/client/src/helpers/desktopnotifications.js @@ -0,0 +1,126 @@ +_kiwi.global.utils.notifications = (function () { + if (!window.Notification) { + return { + allowed: _.constant(false), + requestPermission: _.constant($.Deferred().reject()) + }; + } + + var notifications = { + /** + * Check if desktop notifications have been allowed by the user. + * + * @returns {?Boolean} `true` - they have been allowed. + * `false` - they have been blocked. + * `null` - the user hasn't answered yet. + */ + allowed: function () { + return Notification.permission === 'granted' ? true + : Notification.permission === 'denied' ? false + : null; + }, + + /** + * Ask the user their permission to display desktop notifications. + * This will return a promise which will be resolved if the user allows notifications, or rejected if they blocked + * notifictions or simply closed the dialog. If the user had previously given their preference, the promise will be + * immediately resolved or rejected with their previous answer. + * + * @example + * notifications.requestPermission().then(function () { 'allowed' }, function () { 'not allowed' }); + * + * @returns {Promise} + */ + requestPermission: function () { + var deferred = $.Deferred(); + Notification.requestPermission(function (permission) { + deferred[(permission === 'granted') ? 'resolve' : 'reject'](); + }); + return deferred.promise(); + }, + + /** + * Create a new notification. If the user has not yet given permission to display notifications, they will be asked + * to confirm first. The notification will show afterwards if they allow it. + * + * Notifications implement Backbone.Events (so you can use `on` and `off`). They trigger four different events: + * - 'click' + * - 'close' + * - 'error' + * - 'show' + * + * @example + * notifications + * .create('Cool notification', { icon: 'logo.png' }) + * .on('click', function () { + * window.focus(); + * }) + * .closeAfter(5000); + * + * @param {String} title + * @param {Object} options + * @param {String=} options.body A string representing an extra content to display within the notification + * @param {String=} options.dir The direction of the notification; it can be auto, ltr, or rtl + * @param {String=} options.lang Specify the lang used within the notification. This string must be a valid BCP + * 47 language tag. + * @param {String=} options.tag An ID for a given notification that allows to retrieve, replace or remove it if necessary + * @param {String=} options.icon The URL of an image to be used as an icon by the notification + * @returns {Notifier} + */ + create: function (title, options) { + return new Notifier(title, options); + } + }; + + function Notifier(title, options) { + createNotification.call(this, title, options); + } + _.extend(Notifier.prototype, Backbone.Events, { + closed: false, + _closeTimeout: null, + + /** + * Close the notification after a given number of milliseconds. + * @param {Number} timeout + * @returns {this} + */ + closeAfter: function (timeout) { + if (!this.closed) { + if (this.notification) { + this._closeTimeout = this._closeTimeout || setTimeout(_.bind(this.close, this), timeout); + } else { + this.once('show', _.bind(this.closeAfter, this, timeout)); + } + } + return this; + }, + + /** + * Close the notification immediately. + * @returns {this} + */ + close: function () { + if (this.notification && !this.closed) { + this.notification.close(); + this.closed = true; + } + return this; + } + }); + + function createNotification(title, options) { + switch (notifications.allowed()) { + case true: + this.notification = new Notification(title, options); + _.each(['click', 'close', 'error', 'show'], function (eventName) { + this.notification['on' + eventName] = _.bind(this.trigger, this, eventName); + }, this); + break; + case null: + notifications.requestPermission().done(_.bind(createNotification, this, title, options)); + break; + } + } + + return notifications; +}()); diff --git a/client/src/views/application.js b/client/src/views/application.js index 125430f..b297b86 100644 --- a/client/src/views/application.js +++ b/client/src/views/application.js @@ -345,30 +345,14 @@ _kiwi.view.Application = Backbone.View.extend({ showNotification: function(title, message) { var icon = this.model.get('base_path') + '/assets/img/ico.png', - notification; + notifications = _kiwi.global.utils.notifications; - if (this.has_focus) - return; - - // Different versions of Chrome/firefox have different implimentations - if ('Notification' in window && Notification.permission && Notification.permission === 'granted') { - notification = new Notification(title, {icon: icon, body: message}); - - } else if ('webkitNotifications' in window && webkitNotifications.checkPermission() === 0) { - notification = window.webkitNotifications.createNotification(icon, title, message); - - } else if ('mozNotification' in navigator) { - notification = navigator.mozNotification.createNotification(title, message, icon); + if (!this.has_focus && notifications.allowed()) { + notifications + .create(title, { icon: icon, body: message }) + .closeAfter(5000) + .on('click', _.bind(window.focus, window)); } - - if (!notification) { - // Couldn't find any notification support - return; - } - - setTimeout(function() { - (notification.cancel || notification.close).call(notification); - }, 5000); }, monitorPanelFallback: function() { -- 2.25.1