diff --git a/app/model/Service.js b/app/model/Service.js index 41eb4de2..fe35526b 100644 --- a/app/model/Service.js +++ b/app/model/Service.js @@ -37,7 +37,18 @@ Ext.define('Rambox.model.Service', { name: 'muted' ,type: 'boolean' ,defaultValue: false - },{ + }, + { + name: 'displayTabUnreadCounter', + type: 'boolean', + defaultValue: true + }, + { + name: 'includeInGlobalUnreadCounter', + type: 'boolean', + defaultValue: true + }, + { name: 'trust' ,type: 'boolean' ,defaultValue: false diff --git a/app/store/Services.js b/app/store/Services.js index 81a2f441..62eeae81 100644 --- a/app/store/Services.js +++ b/app/store/Services.js @@ -33,8 +33,10 @@ Ext.define('Rambox.store.Services', { ,icon: service.get('type') !== 'custom' ? 'resources/icons/'+service.get('logo') : ( service.get('logo') === '' ? 'resources/icons/custom.png' : service.get('logo')) ,src: service.get('url') ,type: service.get('type') - ,muted: service.get('muted') - ,enabled: service.get('enabled') + ,muted: service.get('muted'), + includeInGlobalUnreadCounter: service.get('includeInGlobalUnreadCounter'), + displayTabUnreadCounter: service.get('displayTabUnreadCounter'), + enabled: service.get('enabled') ,record: service ,tabConfig: { service: service diff --git a/app/util/Notifier.js b/app/util/Notifier.js new file mode 100644 index 00000000..5d0da406 --- /dev/null +++ b/app/util/Notifier.js @@ -0,0 +1,57 @@ + +/** + * Singleton class for notification dispatching. + */ +Ext.define('Rambox.util.Notifier', { + + singleton: true, + + constructor: function(config) { + + config = config || {}; + + /** + * Returns the notification text depending on the service type. + * + * @param view + * @param count + * @return {*} + */ + function getNotificationText(view, count) { + var text; + switch (Ext.getStore('ServicesList').getById(view.type).get('type')) { + case 'messaging': + text = 'You have ' + Ext.util.Format.plural(count, 'new message', 'new messages') + '.'; + break; + case 'email': + text = 'You have ' + Ext.util.Format.plural(count, 'new email', 'new emails') + '.'; + break; + default: + text = 'You have ' + Ext.util.Format.plural(count, 'new activity', 'new activities') + '.'; + break; + } + return text; + } + + /** + * Dispatches a notification for a specific service. + * + * @param view The view of the service + * @param {number} count The unread count + */ + this.dispatchNotification = function(view, count) { + var text = getNotificationText(view, count); + + var notification = new Notification(view.record.get('name'), { + body: text, + icon: view.tab.icon, + silent: view.record.get('muted') + }); + + notification.onclick = function() { + require('electron').remote.getCurrentWindow().show(); + Ext.cq1('app-main').setActiveTab(view); + }; + } + } +}); diff --git a/app/util/UnreadCounter.js b/app/util/UnreadCounter.js new file mode 100644 index 00000000..d6f98fca --- /dev/null +++ b/app/util/UnreadCounter.js @@ -0,0 +1,76 @@ +/** + * Singleton class to handle the global unread counter. + */ +Ext.define('Rambox.util.UnreadCounter', { + + singleton: true, + + constructor: function(config) { + + config = config || {}; + + /** + * Map for storing the global unread count. + * service id -> unread count + * + * @type {Map} + */ + var unreadCountByService = new Map(); + + /** + * Holds the global unread count for internal usage. + * + * @type {number} + */ + var totalUnreadCount = 0; + + /** + * Sets the application's unread count to tracked unread count. + */ + function updateAppUnreadCounter() { + Rambox.app.setTotalNotifications(totalUnreadCount); + } + + /** + * Returns the global unread count. + * + * @return {number} + */ + this.getTotalUnreadCount = function() { + return totalUnreadCount; + }; + + /** + * Sets the global unread count for a specific service. + * + * @param {*} id Id of the service to set the global unread count for. + * @param {number} unreadCount The global unread count for the service. + */ + this.setUnreadCountForService = function(id, unreadCount) { + unreadCount = parseInt(unreadCount, 10); + + console.log('Rambox.util.UnreadCounter#setUnreadCountForService(' + id + ', ' + unreadCount + ')'); + if (unreadCountByService.has(id)) { + totalUnreadCount -= unreadCountByService.get(id); + } + totalUnreadCount += unreadCount; + unreadCountByService.set(id, unreadCount); + + updateAppUnreadCounter(); + }; + + /** + * Clears the global unread count for a specific service. + * + * @param {*} id Id of the service to clear the global unread count for. + */ + this.clearUnreadCountForService = function(id) { + if (unreadCountByService.has(id)) { + totalUnreadCount -= unreadCountByService.get(id); + } + unreadCountByService['delete'](id); + + updateAppUnreadCounter(); + } + } +}); diff --git a/app/ux/WebView.js b/app/ux/WebView.js index e681b331..053b8bc1 100644 --- a/app/ux/WebView.js +++ b/app/ux/WebView.js @@ -6,14 +6,17 @@ Ext.define('Rambox.ux.WebView',{ ,xtype: 'webview' ,requires: [ - 'Rambox.util.Format' - ] + 'Rambox.util.Format', + 'Rambox.util.Notifier', + 'Rambox.util.UnreadCounter' + ], // private - ,zoomLevel: 0 + zoomLevel: 0, + currentUnreadCount: 0, // CONFIG - ,hideMode: 'offsets' + hideMode: 'offsets' ,initComponent: function(config) { var me = this; @@ -44,8 +47,7 @@ Ext.define('Rambox.ux.WebView',{ ,muted: me.record.get('muted') ,tabConfig: { listeners: { - badgetextchange: me.onBadgeTextChange - ,afterrender : function( btn ) { + afterrender : function( btn ) { btn.el.on('contextmenu', function(e) { btn.showMenu('contextmenu'); e.stopEvent(); @@ -168,42 +170,6 @@ Ext.define('Rambox.ux.WebView',{ return cfg; } - ,onBadgeTextChange: function( tab, badgeText, oldBadgeText ) { - var me = this; - if ( oldBadgeText === null ) oldBadgeText = 0; - var actualNotifications = Rambox.app.getTotalNotifications(); - - oldBadgeText = Rambox.util.Format.stripNumber(oldBadgeText); - badgeText = Rambox.util.Format.stripNumber(badgeText); - - Rambox.app.setTotalNotifications(actualNotifications - oldBadgeText + badgeText); - - // Some services dont have Desktop Notifications, so we add that functionality =) - if ( Ext.getStore('ServicesList').getById(me.type).get('manual_notifications') && oldBadgeText < badgeText && me.record.get('notifications') && !JSON.parse(localStorage.getItem('dontDisturb')) ) { - var text; - switch ( Ext.getStore('ServicesList').getById(me.type).get('type') ) { - case 'messaging': - text = 'You have ' + Ext.util.Format.plural(badgeText, 'new message', 'new messages') + '.'; - break; - case 'email': - text = 'You have ' + Ext.util.Format.plural(badgeText, 'new email', 'new emails') + '.'; - break; - default: - text = 'You have ' + Ext.util.Format.plural(badgeText, 'new activity', 'new activities') + '.'; - break; - } - var not = new Notification(me.record.get('name'), { - body: text - ,icon: tab.icon - ,silent: me.record.get('muted') - }); - not.onclick = function() { - require('electron').remote.getCurrentWindow().show(); - Ext.cq1('app-main').setActiveTab(me); - }; - } - } - ,onAfterRender: function() { var me = this; @@ -302,20 +268,77 @@ Ext.define('Rambox.ux.WebView',{ count = count === '•' ? count : Ext.isArray(count.match(/\d+/g)) ? count.match(/\d+/g).join("") : count.match(/\d+/g); // Some services have special characters. Example: (•) count = count === null ? '0' : count; - me.tab.setBadgeText(Rambox.util.Format.formatNumber(count)); + me.setUnreadCount(count); }); webview.addEventListener('did-get-redirect-request', function( e ) { if ( e.isMainFrame ) webview.loadURL(e.newURL); }); - } + }, - ,reloadService: function(btn) { + setUnreadCount: function(newUnreadCount) { + var me = this; + + if (me.record.get('includeInGlobalUnreadCounter') === true) { + Rambox.util.UnreadCounter.setUnreadCountForService(me.record.get('id'), newUnreadCount); + } else { + Rambox.util.UnreadCounter.clearUnreadCountForService(me.record.get('id')); + } + + me.setTabBadgeText(Rambox.util.Format.formatNumber(newUnreadCount)); + + /** + * Dispatch manual notification if + * • service doesn't have notifications, so Rambox does them + * • count increased + * • not in dnd mode + * • notifications enabled + */ + if (Ext.getStore('ServicesList').getById(me.type).get('manual_notifications') && + me.currentUnreadCount < newUnreadCount && + me.record.get('notifications') && + !JSON.parse(localStorage.getItem('dontDisturb'))) { + Rambox.util.Notifier.dispatchNotification(me, newUnreadCount); + } + + me.currentUnreadCount = newUnreadCount; + }, + + refreshUnreadCount: function() { + this.setUnreadCount(this.currentUnreadCount); + }, + + /** + * Sets the tab badge text depending on the service config param "displayTabUnreadCounter". + * + * @param {string} badgeText + */ + setTabBadgeText: function(badgeText) { + var me = this; + if (me.record.get('displayTabUnreadCounter') === true) { + me.tab.setBadgeText(badgeText); + } else { + me.tab.setBadgeText(''); + } + }, + + /** + * Clears the unread counter for this view: + * • clears the badge text + * • clears the global unread counter + */ + clearUnreadCounter: function() { + var me = this; + me.tab.setBadgeText(''); + Rambox.util.UnreadCounter.clearUnreadCountForService(me.record.get('id')); + }, + + reloadService: function(btn) { var me = this; var webview = me.down('component').el.dom; if ( me.record.get('enabled') ) { - me.tab.setBadgeText(''); + me.clearUnreadCounter(); webview.loadURL(me.src); } } @@ -361,7 +384,8 @@ Ext.define('Rambox.ux.WebView',{ ,setEnabled: function(enabled) { var me = this; - me.tab.setBadgeText(''); + me.clearUnreadCounter(); + me.removeAll(); me.add(me.webViewConstructor(enabled)); if ( enabled ) { diff --git a/app/view/add/Add.js b/app/view/add/Add.js index 8e32e81f..26790f9b 100644 --- a/app/view/add/Add.js +++ b/app/view/add/Add.js @@ -162,8 +162,31 @@ Ext.define('Rambox.view.add.Add',{ ,inputValue: true } ] - } - ,{ + }, + { + xtype: 'fieldset', + title: 'Unread counter', + margin: '10 0 0 0', + items: [ + { + xtype: 'checkbox', + boxLabel: 'Display tab unread counter', + name: 'displayTabUnreadCounter', + checked: me.edit ? me.record.get('displayTabUnreadCounter') : true, + uncheckedValue: false, + inputValue: true + }, + { + xtype: 'checkbox', + boxLabel: 'Include in global unread counter', + name: 'includeInGlobalUnreadCounter', + checked: me.edit ? me.record.get('includeInGlobalUnreadCounter') : true, + uncheckedValue: false, + inputValue: true + } + ] + }, + { xtype: 'fieldset' ,title: 'Advanced' ,margin: '10 0 0 0' diff --git a/app/view/add/AddController.js b/app/view/add/AddController.js index 97ce0a73..5c4e6cbb 100644 --- a/app/view/add/AddController.js +++ b/app/view/add/AddController.js @@ -1,8 +1,12 @@ Ext.define('Rambox.view.add.AddController', { - extend: 'Ext.app.ViewController' - ,alias: 'controller.add-add' + extend: 'Ext.app.ViewController', + alias: 'controller.add-add', - ,doCancel: function( btn ) { + requires: [ + 'Rambox.util.UnreadCounter' + ], + + doCancel: function( btn ) { var me = this; me.getView().close(); @@ -29,37 +33,44 @@ Ext.define('Rambox.view.add.AddController', { ,url: formValues.url ,align: formValues.align ,notifications: formValues.notifications - ,muted: formValues.muted - ,trust: formValues.trust + ,muted: formValues.muted, + displayTabUnreadCounter: formValues.displayTabUnreadCounter, + includeInGlobalUnreadCounter: formValues.includeInGlobalUnreadCounter, + trust: formValues.trust ,js_unread: formValues.js_unread }); + + var view = Ext.getCmp('tab_'+win.record.get('id')); + // Change the title of the Tab - Ext.getCmp('tab_'+win.record.get('id')).setTitle(formValues.serviceName); + view.setTitle(formValues.serviceName); // Change sound of the Tab - Ext.getCmp('tab_'+win.record.get('id')).setAudioMuted(formValues.muted); + view.setAudioMuted(formValues.muted); // Change notifications of the Tab - Ext.getCmp('tab_'+win.record.get('id')).setNotifications(formValues.notifications); + view.setNotifications(formValues.notifications); // Change the icon of the Tab if ( win.record.get('type') === 'custom' && oldData.logo !== formValues.logo ) Ext.getCmp('tab_'+win.record.get('id')).setConfig('icon', formValues.logo === '' ? 'resources/icons/custom.png' : formValues.logo); // Change the URL of the Tab - if ( oldData.url !== formValues.url ) Ext.getCmp('tab_'+win.record.get('id')).setURL(formValues.url); + if ( oldData.url !== formValues.url ) view.setURL(formValues.url); // Change the align of the Tab if ( oldData.align !== formValues.align ) { if ( formValues.align === 'left' ) { - Ext.cq1('app-main').moveBefore(Ext.getCmp('tab_'+win.record.get('id')), Ext.getCmp('tbfill')); + Ext.cq1('app-main').moveBefore(view, Ext.getCmp('tbfill')); } else { - Ext.cq1('app-main').moveAfter(Ext.getCmp('tab_'+win.record.get('id')), Ext.getCmp('tbfill')); + Ext.cq1('app-main').moveAfter(view, Ext.getCmp('tbfill')); } } // Apply the JS Code of the Tab if ( win.down('textarea').isDirty() ) { Ext.Msg.confirm('CUSTOM CODE', 'Rambox needs to reload the service to execute the new JavaScript code. Do you want to do it now?', function( btnId ) { - if ( btnId === 'yes' ) Ext.getCmp('tab_'+win.record.get('id')).reloadService(); + if ( btnId === 'yes' ) view.reloadService(); }); } - Ext.getCmp('tab_'+win.record.get('id')).record = win.record; - Ext.getCmp('tab_'+win.record.get('id')).tabConfig.service = win.record; + view.record = win.record; + view.tabConfig.service = win.record; + + view.refreshUnreadCount(); } else { // Format data if ( win.record.get('url').indexOf('___') >= 0 ) { @@ -73,8 +84,10 @@ Ext.define('Rambox.view.add.AddController', { ,url: formValues.url ,align: formValues.align ,notifications: formValues.notifications - ,muted: formValues.muted - ,trust: formValues.trust + ,muted: formValues.muted, + displayTabUnreadCounter: formValues.displayTabUnreadCounter, + includeInGlobalUnreadCounter: formValues.includeInGlobalUnreadCounter, + trust: formValues.trust ,js_unread: formValues.js_unread }); service.save(); @@ -121,5 +134,4 @@ Ext.define('Rambox.view.add.AddController', { // Make focus to the name field win.down('textfield[name="serviceName"]').focus(true, 100); } - });