Browse Source

Merge branch 'master' into master

pull/560/head
Ramiro Saenz 8 years ago committed by GitHub
parent
commit
0c8e9fc48f
  1. 1
      Backers.md
  2. 37
      README.md
  3. 11
      app/Application.js
  4. 13
      app/model/Service.js
  5. 15
      app/store/Services.js
  6. 22
      app/store/ServicesList.js
  7. 57
      app/util/Notifier.js
  8. 75
      app/util/UnreadCounter.js
  9. 161
      app/ux/WebView.js
  10. 25
      app/view/add/Add.js
  11. 46
      app/view/add/AddController.js
  12. 33
      app/view/preferences/Preferences.js
  13. 24
      app/view/preferences/PreferencesController.js
  14. 19
      electron/main.js
  15. 34
      overrides/layout/container/boxOverflow/Scroller.js
  16. 14
      package.json
  17. BIN
      resources/icons/workplace.png
  18. BIN
      resources/icons/xing.png
  19. 28
      resources/js/rambox-service-api.js

1
Backers.md

@ -3,3 +3,4 @@
[Martin Grünbaum](https://github.com/alathon)
Ivan Toshkov
[Simon Joda Stößer](https://github.com/SimJoSt)

37
README.md

@ -47,6 +47,7 @@
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/gmail.png" alt="Gmail" title="Gmail">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/inbox.png" alt="Inbox" title="Inbox">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/hipchat.png" alt="HipChat" title="HipChat">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/chatwork.png" alt="ChatWork" title="ChatWork">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/groupme.png" alt="GroupMe" title="GroupMe">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/grape.png" alt="Grape" title="Grape">
@ -57,6 +58,7 @@
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/outlook.png" alt="Outlook" title="Outlook">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/outlook365.png" alt="Outlook 365" title="Outlook 365">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/tutanota.png" alt="TutaNota" title="TutaNota">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/hushmail.png" alt="Hushmail" title="Hushmail">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/bearychat.png" alt="BearyChat" title="BearyChat">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/aol.png" alt="Aol" title="Aol">
@ -67,6 +69,7 @@
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/missive.png" alt="Missive" title="Missive">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/yahoo.png" alt="Yahoo! Mail" title="Yahoo! Mail">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/ryver.png" alt="Ryver" title="Ryver">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/yandex.png" alt="Yandex Mail" title="Yandex Mail">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/dasher.png" alt="Dasher" title="Dasher">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/dingtalk.png" alt="DingTalk" title="DingTalk">
@ -77,6 +80,7 @@
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/yahoomessenger.png" alt="Yahoo! Messenger" title="Yahoo! Messenger">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/mysms.png" alt="mysms" title="mysms">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/icq.png" alt="ICQ" title="ICQ">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/tweetdeck.png" alt="TweetDeck" title="TweetDeck">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/zinc.png" alt="Zinc" title="Zinc">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/freenode.png" alt="FreeNode" title="FreeNode">
@ -87,6 +91,7 @@
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/horde.png" alt="Horde" title="Horde">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/squirrelmail.png" alt="SquirrelMail" title="SquirrelMail">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/zimbra.png" alt="Zimbra" title="Zimbra">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/hootsuite.png" alt="Hootsuite" title="Hootsuite">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/amium.png" alt="Amium" title="Amium">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/rainloop.png" alt="RainLoop" title="RainLoop">
@ -97,6 +102,7 @@
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/crisp.png" alt="Crisp" title="Crisp">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/flock.png" alt="Flock" title="Flock">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/openmailbox.png" alt="Openmailbox" title="Openmailbox">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/typetalk.png" alt="Typetalk" title="Typetalk">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/drift.png" alt="Drift" title="Drift">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/mmmelon.png" alt="mmmelon" title="mmmelon">
@ -107,7 +113,9 @@
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/riot.png" alt="Riot" title="Riot">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/pushbullet.png" alt="Pushbullet" title="Pushbullet">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/movim.png" alt="Movim" title="Movim">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/kaiwa.png" alt="Kaiwa" title="Kaiwa">
<img width="80" align="left" src="https://raw.githubusercontent.com/saenzramiro/rambox/master/resources/icons/xing.png" alt="XING" title="XING">
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
@ -152,6 +160,35 @@ Want to report a bug, request a feature, contribute to or translate Rambox? We n
If you're comfortable getting up and running from a `git clone`, this method is for you.
## Adding a service
The available services are stored in the [ServiceList.js](app/store/ServicesList.js).
Structure of a service entry:
|Name|Description|Required|
|---|---|---|
|id|Unique identifier for the service, e.g. "slack"|yes|
|logo|File name of the service logo located in "/resources/icons/", e.g. "slack.png"|yes|
|name|Visible name for the service, e.g. "Slack"|yes|
|description|A short description of the service, e.g. "Slack brings all your communication together..."|yes|
|url|URL of the service, e.g. "https://\_\_\_.slack.com/". "\_\_\_" may be used as a placeholder, that can be configured when adding a service.|yes|
|type|Defines the type of the service. Must be one of `email` or `messaging`.|yes|
|allow_popups|Set to `true` to allow popup windows for the service.|no|
|note|Additional info to display when adding the service.|no|
|manual_notifications|Set to `true` to let Rambox trigger notifications. Can be used for services that doesn't support browser notifications.|no|
|js_unread|JavaScript code for setting the unread count (see below).|no|
|dont_update_unread_from_title|Set to `true` to prevent updating the unread count from the window title (see below).|no|
### Setting the unread count
While there is also a way to set the unread count by adding ` (COUNT)` to the window title, this describes the preferred way of doing it:
First set `dont_update_unread_from_title` in the service config to `true`.
Code provided by `js_unread` will be injected into the service website.
You can retrieve the unread count in this JavaScript code e.g. by parsing elements.
Set the unread count by calling `rambox.setUnreadCount(COUNT)` or clear it by calling `rambox.clearUnreadCount()`.
#### Technologies:
* Sencha Ext JS 5.1.1.451

11
app/Application.js

@ -44,9 +44,14 @@ Ext.define('Rambox.Application', {
var tabPanel = Ext.cq1('app-main');
var activeIndex = tabPanel.items.indexOf(tabPanel.getActiveTab());
var i = activeIndex + 1;
if ( i >= tabPanel.items.items.length - 1 ) i = 0;
while ( tabPanel.items.items[i].id === 'tbfill' ) i++;
tabPanel.setActiveTab( i );
// "cycle" (go to the start) when the end is reached or the end is the spacer "tbfill"
if (i === tabPanel.items.items.length || i === tabPanel.items.items.length - 1 && tabPanel.items.items[i].id === 'tbfill') i = 0;
// skip spacer
while (tabPanel.items.items[i].id === 'tbfill') i++;
tabPanel.setActiveTab(i);
}
}
,{

13
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

15
app/store/Services.js

@ -26,6 +26,19 @@ 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;
}
var cfg = {
xtype: 'webview'
,id: 'tab_'+service.get('id')
@ -34,6 +47,8 @@ Ext.define('Rambox.store.Services', {
,src: service.get('url')
,type: service.get('type')
,muted: service.get('muted')
,includeInGlobalUnreadCounter: service.get('includeInGlobalUnreadCounter')
,displayTabUnreadCounter: service.get('displayTabUnreadCounter')
,enabled: service.get('enabled')
,record: service
,tabConfig: {

22
app/store/ServicesList.js

@ -95,7 +95,7 @@ Ext.define('Rambox.store.ServicesList', {
,url: 'https://web.telegram.org/'
,type: 'messaging'
,titleBlink: true
,js_unread: 'function checkUnread(){var e=document.getElementsByClassName("im_dialog_badge badge"),t=0;for(i=0;i<e.length;i++)t+=parseInt(e[i].innerHTML.trim());updateBadge(t)}function updateBadge(e){document.title="("+e+") RamboxService"}setInterval(checkUnread,3000);'
,js_unread: 'function checkUnread(){var e=document.getElementsByClassName("im_dialog_badge badge"),t=0;for(i=0;i<e.length;i++)if(!e[i].classList.contains("im_dialog_badge_muted")){t+=parseInt(e[i].innerHTML.trim())}}function updateBadge(e){document.title="("+e+") RamboxService"}setInterval(checkUnread,3000);'
},
{
id: 'wechat'
@ -459,7 +459,7 @@ Ext.define('Rambox.store.ServicesList', {
,type: 'email'
},
{
id: ' irccloud'
id: 'irccloud'
,logo: 'irccloud.png'
,name: 'IRCCloud'
,description: 'IRCCloud is a modern IRC client that keeps you connected, with none of the baggage.'
@ -660,6 +660,24 @@ Ext.define('Rambox.store.ServicesList', {
,description: 'Unified multi-channel messaging for businesses, bots and software makers.'
,url: 'https://app.smooch.io/'
,type: 'messaging'
},
{
id: 'xing'
,logo: 'xing.png'
,name: 'XING'
,description: 'Career-oriented social networking'
,url: 'https://www.xing.com/messages/conversations'
,type: 'messaging'
,js_unread: '(function() { let originalTitle = document.title; function checkUnread() { let count = null; let notificationElement = document.querySelector(\'[data-update="unread_conversations"]\'); if (notificationElement && notificationElement.style.display !== \'none\') { count = parseInt(notificationElement.textContent.trim(), 10); } updateBadge(count); } function updateBadge(count) { if (count && count >= 1) { rambox.setUnreadCount(count); } else { rambox.clearUnreadCount(); } } setInterval(checkUnread, 3000); checkUnread(); })();'
,dont_update_unread_from_title: true
},
{
id: 'Workplace'
,logo: 'workplace.png'
,name: 'Workplace by Facebook'
,description: 'Connect everyone in your company and turn ideas into action. Through group discussion, a personalised News Feed, and voice and video calling, work together and get more done. Workplace is an ad-free space, separate from your personal Facebook account.'
,url: 'https://___.facebook.com/'
,type: 'messaging'
}
]
});

57
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);
};
}
}
});

75
app/util/UnreadCounter.js

@ -0,0 +1,75 @@
/**
* 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);
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();
}
}
});

161
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();
@ -158,7 +160,8 @@ Ext.define('Rambox.ux.WebView',{
,autosize: 'on'
,disablewebsecurity: 'on'
,blinkfeatures: 'ApplicationCache,GlobalCacheStorage'
,useragent: Ext.getStore('ServicesList').getById(me.record.get('type')).get('userAgent')
,useragent: Ext.getStore('ServicesList').getById(me.record.get('type')).get('userAgent'),
preload: './resources/js/rambox-service-api.js'
}
};
@ -168,42 +171,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;
@ -274,7 +241,7 @@ Ext.define('Rambox.ux.WebView',{
// Injected code to detect new messages
if ( me.record ) {
var js_unread = Ext.getStore('ServicesList').getById(me.record.get('type') === 'office365' ? 'outlook365' : me.record.get('type')).get('js_unread');
var js_unread = Ext.getStore('ServicesList').getById(me.record.get('type')).get('js_unread');
js_unread = js_unread + me.record.get('js_unread');
if ( js_unread !== '' ) {
console.groupCollapsed(me.record.get('type').toUpperCase() + ' - JS Injected to Detect New Messages');
@ -296,14 +263,54 @@ Ext.define('Rambox.ux.WebView',{
webview.executeJavaScript('document.body.scrollTop=0;');
});
webview.addEventListener('ipc-message', function(event) {
var channel = event.channel;
switch (channel) {
case 'rambox.setUnreadCount':
handleSetUnreadCount(event);
break;
case 'rambox.clearUnreadCount':
handleClearUnreadCount(event);
break;
}
/**
* Handles 'rambox.clearUnreadCount' messages.
* Clears the unread count.
*/
function handleClearUnreadCount() {
me.tab.setBadgeText('');
}
/**
* Handles 'rambox.setUnreadCount' messages.
* Sets the badge text if the event contains an integer as first argument.
*
* @param event
*/
function handleSetUnreadCount(event) {
if (Array.isArray(event.args) === true && event.args.length > 0) {
var count = event.args[0];
if (count === parseInt(count, 10)) {
me.tab.setBadgeText(Rambox.util.Format.formatNumber(count));
}
}
}
});
/**
* Register page title update event listener only for services that don't prevent it by setting 'dont_update_unread_from_title' to true.
*/
if (Ext.getStore('ServicesList').getById(me.record.get('type')).get('dont_update_unread_from_title') !== true) {
webview.addEventListener("page-title-updated", function(e) {
var count = e.title.match(/\(([^)]+)\)/); // Get text between (...)
count = count ? count[1] : '0';
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);
@ -314,14 +321,71 @@ Ext.define('Rambox.ux.WebView',{
var SpellCheckProvider = require('electron-spell-check-provider');
webFrame.setSpellCheckProvider('en-US', true, new SpellCheckProvider('en-US'));
}
},
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('');
}
},
,reloadService: function(btn) {
/**
* 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);
}
}
@ -367,7 +431,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 ) {

25
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'

46
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);
}
});

33
app/view/preferences/Preferences.js

@ -67,17 +67,28 @@ Ext.define('Rambox.view.preferences.Preferences',{
,boxLabel: 'Show in Taskbar'
,value: config.skip_taskbar
,reference: 'skipTaskbar'
,hidden: process.platform !== 'win32'
}
,{
xtype: 'checkbox'
,name: 'keep_in_taskbar_on_close'
,boxLabel: 'Keep Rambox in the Taskbar when close it'
,value: config.keep_in_taskbar_on_close
,bind: { disabled: '{!skipTaskbar.checked}' }
,hidden: process.platform !== 'win32'
}
,{
,hidden: process.platform === 'darwin'
},
{
xtype: 'combo',
name: 'window_close_behavior',
fieldLabel: 'When closing the main window',
labelAlign: 'top',
value: config.window_close_behavior,
displayField: 'label',
valueField: 'value',
editable: false,
store: Ext.create('Ext.data.Store', {
fields: ['value', 'label'],
data : [
{ 'value': 'keep_in_tray', 'label': 'Keep in tray' },
{ 'value': 'keep_in_tray_and_taskbar', 'label': 'Keep in tray and taskbar' },
{ 'value': 'quit', 'label': 'Quit' }
]
}),
hidden: process.platform === 'darwin'
},
{
xtype: 'checkbox'
,name: 'always_on_top'
,boxLabel: 'Always on top'

24
app/view/preferences/PreferencesController.js

@ -13,12 +13,28 @@ Ext.define('Rambox.view.preferences.PreferencesController', {
var values = me.getView().down('form').getForm().getFieldValues();
// Master Password
if ( values.master_password && (Ext.isEmpty(values.master_password1) || Ext.isEmpty(values.master_password2)) ) return;
if ( values.master_password && (values.master_password1 !== values.master_password2) ) return;
if ( values.master_password ) values.master_password = Rambox.util.MD5.encypt(values.master_password1);
// master password activated and only one of the fields "password" or "password confirmation" filled
if (values.master_password === true &&
(Ext.isEmpty(values.master_password1) === false && Ext.isEmpty(values.master_password2) === true ||
Ext.isEmpty(values.master_password1) === true && Ext.isEmpty(values.master_password2) === false)) return;
// password and confirmation don't match
if (values.master_password === true && (values.master_password1 !== values.master_password2)) return;
// master password activated and changed
if (values.master_password === true &&
Ext.isEmpty(values.master_password1) === false &&
Ext.isEmpty(values.master_password2) === false) {
values.master_password = Rambox.util.MD5.encypt(values.master_password1);
delete values.master_password1;
delete values.master_password2;
}
// prevent overwriting password when unchanged
if (values.master_password === true) {
delete values.master_password;
}
// Proxy
if ( values.proxy && (Ext.isEmpty(values.proxyHost) || Ext.isEmpty(values.proxyPort)) ) return;

19
electron/main.js

@ -24,8 +24,7 @@ const config = new Config({
,hide_menu_bar: false
,skip_taskbar: true
,auto_launch: !isDev
// On Linux false because it's uncommon for apps on linux to stay in the taskbar on close
,keep_in_taskbar_on_close: process.platform !== 'linux'
,window_close_behavior: 'keep_in_tray'
,start_minimized: false
,systemtray_indicator: true
,master_password: false
@ -45,7 +44,7 @@ const config = new Config({
// Configure AutoLaunch
const appLauncher = new AutoLaunch({
name: 'Rambox'
name: process.platform === 'darwin' ? 'Rambox.app' : 'Rambox'
,isHiddenOnLaunch: config.get('start_minimized')
});
config.get('auto_launch') && !isDev ? appLauncher.enable() : appLauncher.disable();
@ -200,11 +199,19 @@ function createWindow () {
app.hide();
break;
case 'linux':
config.get('keep_in_taskbar_on_close') ? mainWindow.hide() : app.quit();
break;
case 'win32':
default:
config.get('keep_in_taskbar_on_close') ? mainWindow.minimize() : mainWindow.hide();
switch (config.get('window_close_behavior')) {
case 'keep_in_tray':
mainWindow.hide();
break;
case 'keep_in_tray_and_taskbar':
mainWindow.minimize();
break;
case 'quit':
app.quit();
break;
}
break;
}
}

34
overrides/layout/container/boxOverflow/Scroller.js

@ -0,0 +1,34 @@
/**
* Per default scrolling the tab bar moves the tabs 20 pixels.
* To improve the usability of the tab bar this value is increased for Rambox.
* Also animations are enabled, so the user understands what's going on.
*/
Ext.define('Rambox.overrides.layout.container.boxOverflow.Scroller', {
override: 'Ext.layout.container.boxOverflow.Scroller',
scrollIncrement: 250,
wheelIncrement: 50,
animateScroll: true,
scrollDuration: 250,
/**
* In difference to the overridden function this one enables scroll animations.
*
* @private
* Scrolls to the left by the configured amount
*/
scrollLeft: function() {
this.scrollBy(-this.scrollIncrement);
},
/**
* In difference to the overridden function this one enables scroll animations.
*
* @private
* Scrolls to the right by the configured amount
*/
scrollRight: function() {
this.scrollBy(this.scrollIncrement);
}
});

14
package.json

@ -61,6 +61,7 @@
},
"win": {
"title": "Rambox",
"target": [
"squirrel",
"zip"
@ -88,19 +89,18 @@
},
"devDependencies": {
"asar": "^0.12.1",
"electron": "1.4.7",
"electron-builder": "6.5.2",
"electron-rebuild": "^1.5.5",
"electron-builder": "11.3.0",
"electron": "1.4.14",
"electron-squirrel-startup": "^1.0.0"
},
"dependencies": {
"auto-launch": "4.0.0",
"electron-config": "0.2.1",
"electron-is-dev": "^0.1.1",
"electron-spell-check-provider": "^1.0.0",
"firebase": "^3.0.5",
"electron-spell-check-provider": "^1.0.0",
"firebase-token-generator": "^2.0.0",
"tmp": "0.0.28",
"mime": "^1.3.4",
"tmp": "0.0.28"
"electron-is-dev": "^0.1.1",
"electron-config": "0.2.1"
}
}

BIN
resources/icons/workplace.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
resources/icons/xing.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

28
resources/js/rambox-service-api.js

@ -0,0 +1,28 @@
/**
* This file is loaded in the service web views to provide a Rambox API.
*/
const { ipcRenderer } = require('electron');
/**
* Make the Rambox API available via a global "rambox" variable.
*
* @type {{}}
*/
window.rambox = {};
/**
* Sets the unraed count of the tab.
*
* @param {*} count The unread count
*/
window.rambox.setUnreadCount = function(count) {
ipcRenderer.sendToHost('rambox.setUnreadCount', count);
};
/**
* Clears the unread count.
*/
window.rambox.clearUnreadCount = function() {
ipcRenderer.sendToHost('rambox.clearUnreadCount');
}
Loading…
Cancel
Save