diff --git a/app/Application.js b/app/Application.js index cd9ababf..5c8ba338 100644 --- a/app/Application.js +++ b/app/Application.js @@ -35,7 +35,7 @@ Ext.define('Rambox.Application', { Ext.Loader.loadScript({url: Ext.util.Format.format("ext/packages/ext-locale/build/ext-locale-{0}.js", localStorage.getItem('locale-auth0') || 'en')}); // Initialize Auth0 - Rambox.ux.Auth0.init(); + if ( auth0Cfg.clientID !== '' && auth0Cfg.domain !== '' ) Rambox.ux.Auth0.init(); // Set cookies to help Tooltip.io messages segmentation Ext.util.Cookies.set('version', require('electron').remote.app.getVersion()); diff --git a/app/store/Services.js b/app/store/Services.js index 134efc34..506f9a4b 100644 --- a/app/store/Services.js +++ b/app/store/Services.js @@ -26,19 +26,6 @@ Ext.define('Rambox.store.Services', { var servicesLeft = []; var servicesRight = []; store.each(function(service) { - // Fix some services with bad IDs - // TODO: Remove in next release - switch ( service.get('type') ) { - case 'office365': - service.set('type', 'outlook365'); - break; - case ' irccloud': - service.set('type', 'irccloud'); - break; - default: - break; - } - // If the service is disabled, we dont add it to tab bar if ( !service.get('enabled') ) return; @@ -65,6 +52,19 @@ Ext.define('Rambox.store.Services', { if ( !Ext.isEmpty(servicesLeft) ) Ext.cq1('app-main').insert(1, servicesLeft); if ( !Ext.isEmpty(servicesRight) ) Ext.cq1('app-main').add(servicesRight); + // Set default active service + const config = ipc.sendSync('getConfig'); + switch ( config.default_service ) { + case 'last': + Ext.cq1('app-main').setActiveTab(localStorage.getItem('last_active_service')); + break; + case 'ramboxTab': + break; + default: + if ( Ext.getCmp('tab_'+config.default_service) ) Ext.cq1('app-main').setActiveTab('tab_'+config.default_service); + break; + } + store.suspendEvent('load'); Ext.cq1('app-main').resumeEvent('add'); } diff --git a/app/store/ServicesList.js b/app/store/ServicesList.js index cfb1da58..ce764b08 100644 --- a/app/store/ServicesList.js +++ b/app/store/ServicesList.js @@ -38,7 +38,7 @@ Ext.define('Rambox.store.ServicesList', { ,description: locale['services[1]'] ,url: 'https://___.slack.com/' ,type: 'messaging' - ,js_unread: 'function checkUnread(){var e=$(".p-channel_sidebar__channel--unread").length,a=0;$(".p-channel_sidebar__badge").each(function(){a+=isNaN(parseInt($(this).html()))?0:parseInt($(this).html())}),updateBadge(e,a)}function updateBadge(e,a){var n=a>0?"("+a+") ":e>0?"(•) ":"";document.title=n+originalTitle}var originalTitle=document.title;setInterval(checkUnread,3e3);' + ,js_unread: 'function checkUnread(){var e=$(".p-channel_sidebar__channel--unread:not(.p-channel_sidebar__channel--muted)").length,a=0;$(".p-channel_sidebar__badge").each(function(){a+=isNaN(parseInt($(this).html()))?0:parseInt($(this).html())}),updateBadge(e,a)}function updateBadge(e,a){var n=a>0?"("+a+") ":e>0?"(•) ":"";document.title=n+originalTitle}var originalTitle=document.title;setInterval(checkUnread,3e3);' }, { id: 'noysi' @@ -188,6 +188,8 @@ Ext.define('Rambox.store.ServicesList', { ,titleBlink: true ,js_unread: 'function checkUnread(){var a=document.getElementsByClassName("guild unread").length,b=0,c=document.getElementsByClassName("badge");for(i=0;i0?"("+b+") ":a>0?"(•) ":"";document.title=c+originalTitle}var originalTitle=document.title;setInterval(checkUnread,3e3);' ,note: 'To enable desktop notifications, you have to go to Options inside Discord.' + ,dont_update_unread_from_title: true + ,userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36' }, { id: 'outlook' @@ -837,6 +839,15 @@ Ext.define('Rambox.store.ServicesList', { ,description: 'Kune is a web tool, based on Apache Wave, for creating environments of constant inter-communication, collective intelligence, knowledge and shared work.' ,url: 'https://kune.cc' ,type: 'messaging' + }, + { + id: 'googlevoice' + ,logo: 'googlevoice.png' + ,name: 'Google Voice' + ,description: 'A free phone number for life. Stay in touch from any screen. Use your free number to text, call, and check voicemail all from one app. Plus, Google Voice works on all of your devices so you can connect and communicate how you want.' + ,url: 'https://voice.google.com' + ,type: 'messaging' + ,js_unread: 'function parseIntOrZero(e){return isNaN(parseInt(e))?0:parseInt(e)}function checkUnread(){var e=document.querySelector(".msgCount"),n=0;e?n=parseIntOrZero(e.innerHTML.replace(/[\(\) ]/gi,"")):["Messages","Calls","Voicemail"].forEach(function(e){var r=document.querySelector(\'gv-nav-button[tooltip="\'+e+\'"] div[aria-label="Unread count"]\');r&&(n+=parseIntOrZero(r.innerHTML))}),updateBadge(n)}function updateBadge(e){var n=e>0?"("+e+") ":"";document.title=n+originalTitle}var originalTitle=document.title;setInterval(checkUnread,3000);' } ] }); diff --git a/app/util/IconLoader.js b/app/util/IconLoader.js new file mode 100644 index 00000000..b4f0349b --- /dev/null +++ b/app/util/IconLoader.js @@ -0,0 +1,32 @@ +/** + * Singleton class to handle the global unread counter. + */ +Ext.define('Rambox.util.IconLoader', { + + singleton: true, + + constructor: function(config) { + + config = config || {}; + + /** + * Sets the icon for a specific service. + * + * @param {*} id Id of the service + */ + this.loadServiceIconUrl = function (service, webview) { + switch (service.type) { + case 'slack': + webview.executeJavaScript("(()=>{let a=document.querySelector('.team_icon');if(!a){const d=document.querySelector('#team_menu');d&&(d.click(),a=document.querySelector('.team_icon'))}if(!a)return!1;const{style:{backgroundImage:b}}=a,c=document.createEvent('MouseEvents');return c.initEvent('mousedown',!0,!0),document.querySelector('.client_channels_list_container').dispatchEvent(c),b.slice(5,-2)})();", + false, + function (backgroundImage) { + if (backgroundImage) { + service.fireEvent('iconchange', service, backgroundImage, service.icon); + } + } + ); + break; + } + }; + } +}); diff --git a/app/ux/WebView.js b/app/ux/WebView.js index da8c6f8b..a512695c 100644 --- a/app/ux/WebView.js +++ b/app/ux/WebView.js @@ -9,6 +9,7 @@ Ext.define('Rambox.ux.WebView',{ 'Rambox.util.Format' ,'Rambox.util.Notifier' ,'Rambox.util.UnreadCounter' + ,'Rambox.util.IconLoader' ] // private @@ -251,6 +252,8 @@ Ext.define('Rambox.ux.WebView',{ // Apply saved zoom level webview.setZoomLevel(me.record.get('zoomLevel')); + + Rambox.util.IconLoader.loadServiceIconUrl(me, webview); }); // Open links in default browser @@ -338,7 +341,7 @@ Ext.define('Rambox.ux.WebView',{ require('electron').shell.openExternal(e.url); } }); - + webview.addEventListener('will-navigate', function(e, url) { e.preventDefault(); }); @@ -658,4 +661,12 @@ Ext.define('Rambox.ux.WebView',{ me.record.set('zoomLevel', me.zoomLevel); } } + + ,getWebView: function() { + if ( this.record.get('enabled') ) { + return this.down('component').el.dom; + } else { + return false; + } + } }); diff --git a/app/view/main/MainController.js b/app/view/main/MainController.js index 7fc34cda..c7fe5a46 100644 --- a/app/view/main/MainController.js +++ b/app/view/main/MainController.js @@ -10,6 +10,8 @@ Ext.define('Rambox.view.main.MainController', { // Set Google Analytics event ga_storage._trackPageview('/index.html', 'main'); + localStorage.setItem('last_active_service', newTab.id); + if ( newTab.id === 'ramboxTab' ) { if ( Rambox.app.getTotalNotifications() > 0 ) { document.title = 'Rambox ('+ Rambox.app.getTotalNotifications() +')'; @@ -69,7 +71,7 @@ Ext.define('Rambox.view.main.MainController', { Ext.getCmp('tab_'+e.record.get('id')).setTitle(e.record.get('name')); } - ,onEnableDisableService: function(cc, rowIndex, checked) { + ,onEnableDisableService: function(cc, rowIndex, checked, obj, hideTab) { var rec = Ext.getStore('Services').getAt(rowIndex); if ( !checked ) { @@ -87,8 +89,9 @@ Ext.define('Rambox.view.main.MainController', { ,displayTabUnreadCounter: rec.get('displayTabUnreadCounter') ,enabled: rec.get('enabled') ,record: rec + ,hidden: hideTab ,tabConfig: { - service: rec + service: rec } }); } @@ -100,32 +103,63 @@ Ext.define('Rambox.view.main.MainController', { }); } - ,removeServiceFn: function(serviceId) { + ,removeServiceFn: function(serviceId, total, actual) { + var me = this; if ( !serviceId ) return false; - // Get Tab - var tab = Ext.getCmp('tab_'+serviceId); // Get Record var rec = Ext.getStore('Services').getById(serviceId); - // Clear all trash data - if ( rec.get('enabled') && tab.down('component').el ) { - tab.down('component').el.dom.getWebContents().session.clearCache(Ext.emptyFn); - tab.down('component').el.dom.getWebContents().session.clearStorageData({}, Ext.emptyFn); + if ( !rec.get('enabled') ) { + rec.set('enabled', true); + me.onEnableDisableService(null, Ext.getStore('Services').indexOf(rec), true, null, true); + Ext.defer(function() { + // Get Tab + var tab = Ext.getCmp('tab_'+serviceId); + // Clear all trash data + const webview = tab.getWebView(); + + webview.addEventListener("did-finish-load", function() { + clearData(webview, tab); + }); + }, 1000); + } else { + // Get Tab + var tab = Ext.getCmp('tab_'+serviceId); + // Clear all trash data + const webview = tab.getWebView(); + clearData(webview, tab); } - // Remove record from localStorage - Ext.getStore('Services').remove(rec); - - // Close tab - tab.close(); + const config = ipc.sendSync('getConfig'); + if ( config.default_service === rec.get('id') ) ipc.send('setConfig', Ext.apply(config, { default_service: 'ramboxTab' })); + + function clearData(webview, tab) { + webview.getWebContents().clearHistory(); + webview.getWebContents().session.flushStorageData(); + webview.getWebContents().session.clearCache(function() { + webview.getWebContents().session.clearStorageData(function() { + webview.getWebContents().session.cookies.flushStore(function() { + // Remove record from localStorage + Ext.getStore('Services').remove(rec); + // Close tab + tab.close(); + // Close waiting message + if ( total === actual ) Ext.Msg.hide(); + }); + }); + }); + } } ,removeService: function( gridView, rowIndex, colIndex, col, e, rec, rowEl ) { var me = this; Ext.Msg.confirm(locale['app.window[12]'], locale['app.window[13]']+' '+rec.get('name')+'?', function(btnId) { - if ( btnId === 'yes' ) me.removeServiceFn(rec.get('id')); + if ( btnId === 'yes' ) { + Ext.Msg.wait('Please wait until we clear all.', 'Removing...'); + me.removeServiceFn(rec.get('id'), 1, 1); + } }); } @@ -140,8 +174,11 @@ Ext.define('Rambox.view.main.MainController', { if ( btnId === 'yes' ) { Ext.cq1('app-main').suspendEvent('remove'); Ext.getStore('Services').load(); + Ext.Msg.wait('Please wait until we clear all.', 'Removing...'); + const count = Ext.getStore('Services').getCount(); + var i = 1; Ext.Array.each(Ext.getStore('Services').collect('id'), function(serviceId) { - me.removeServiceFn(serviceId); + me.removeServiceFn(serviceId, count, i++); }); if ( Ext.isFunction(callback) ) callback(); Ext.cq1('app-main').resumeEvent('remove'); @@ -152,7 +189,7 @@ Ext.define('Rambox.view.main.MainController', { Ext.cq1('app-main').suspendEvent('remove'); Ext.getStore('Services').load(); Ext.Array.each(Ext.getStore('Services').collect('id'), function(serviceId) { - me.removeServiceFn(serviceId); + me.removeServiceFn(serviceId, 1, 2); }); if ( Ext.isFunction(callback) ) callback(); Ext.cq1('app-main').resumeEvent('remove'); @@ -386,6 +423,13 @@ Ext.define('Rambox.view.main.MainController', { ] } ] + ,listeners: { + render: function(win) { + win.getEl().on('click', function() { + win.down('textfield').focus(100); + }); + } + } }).show(); winLock.down('textfield').focus(1000); } diff --git a/app/view/preferences/Preferences.js b/app/view/preferences/Preferences.js index 4e9541c8..3eb40737 100644 --- a/app/view/preferences/Preferences.js +++ b/app/view/preferences/Preferences.js @@ -38,6 +38,16 @@ Ext.define('Rambox.view.preferences.Preferences',{ ,initComponent: function() { var config = ipc.sendSync('getConfig'); + var defaultServiceOptions = []; + defaultServiceOptions.push({ value: 'ramboxTab', label: 'Rambox Tab' }); + defaultServiceOptions.push({ value: 'last', label: 'Last Active Service' }); + Ext.getStore('Services').each(function(rec) { + defaultServiceOptions.push({ + value: rec.get('id') + ,label: rec.get('name') + }); + }); + this.items = [ { xtype: 'form' @@ -114,6 +124,22 @@ Ext.define('Rambox.view.preferences.Preferences',{ ,value: config.hide_menu_bar ,hidden: process.platform !== 'win32' } + ,{ + xtype: 'combo' + ,name: 'default_service' + ,fieldLabel: 'Default service to display when Rambox starts' + ,labelAlign: 'top' + //,width: 380 + //,labelWidth: 105 + ,value: config.default_service + ,displayField: 'label' + ,valueField: 'value' + ,editable: false + ,store: Ext.create('Ext.data.Store', { + fields: ['value', 'label'] + ,data: defaultServiceOptions + }) + } ,{ xtype: 'combo' ,name: 'window_display_behavior' diff --git a/electron/main.js b/electron/main.js index 3c571a1d..5a12b003 100644 --- a/electron/main.js +++ b/electron/main.js @@ -34,6 +34,7 @@ const config = new Config({ ,proxyPort: '' ,locale: 'en' ,enable_hidpi_support: false + ,default_service: 'ramboxTab' ,x: undefined ,y: undefined @@ -162,6 +163,15 @@ function createWindow () { if ( !config.get('start_minimized') && config.get('maximized') ) mainWindow.maximize(); + // Check if the window its outside of the view (ex: multi monitor setup) + const { positionOnScreen } = require('./utils/positionOnScreen'); + const inBounds = positionOnScreen([config.get('x'), config.get('y')]); + if ( inBounds ) { + mainWindow.setPosition(config.get('x'), config.get('y')); + } else { + mainWindow.center(); + } + process.setMaxListeners(10000); // Open the DevTools. diff --git a/electron/utils/positionOnScreen.js b/electron/utils/positionOnScreen.js new file mode 100644 index 00000000..35ca487b --- /dev/null +++ b/electron/utils/positionOnScreen.js @@ -0,0 +1,18 @@ +const { screen } = require('electron'); + +const positionOnScreen = (position) => { + let inBounds = false; + if (position) { + screen.getAllDisplays().forEach((display) => { + if (position[0] >= display.workArea.x && + position[0] <= display.workArea.x + display.workArea.width && + position[1] >= display.workArea.y && + position[1] <= display.workArea.y + display.workArea.height) { + inBounds = true; + } + }); + } + return inBounds; +}; + +module.exports = {positionOnScreen}; diff --git a/resources/icons/googlevoice.png b/resources/icons/googlevoice.png new file mode 100644 index 00000000..76682773 Binary files /dev/null and b/resources/icons/googlevoice.png differ