diff --git a/app.js b/app.js
index 0540da48..c7a3f9cb 100644
--- a/app.js
+++ b/app.js
@@ -28,6 +28,10 @@ ipc.on('showAbout', function(event, message) {
ipc.on('showPreferences', function(event, message) {
!Ext.cq1('preferences') ? Ext.create('Rambox.view.preferences.Preferences').show() : '';
});
+ipc.on('grantPermissions', async function() {
+ await require('electron').remote.systemPreferences.askForMediaAccess('microphone');
+ await require('electron').remote.systemPreferences.askForMediaAccess('camera');
+});
ipc.on('autoUpdater:check-update', function() {
Rambox.app.checkUpdate();
});
diff --git a/app/Application.js b/app/Application.js
index 830a12b3..17dbfe80 100644
--- a/app/Application.js
+++ b/app/Application.js
@@ -60,6 +60,48 @@ Ext.define('Rambox.Application', {
})
})();
+ if ( !localStorage.getItem('hideMacPermissions') && process.platform === 'darwin' && (require('electron').remote.systemPreferences.getMediaAccessStatus('microphone') !== 'granted' || require('electron').remote.systemPreferences.getMediaAccessStatus('camera') !== 'granted') ) {
+ console.info('Checking mac permissions...');
+ Ext.cq1('app-main').addDocked({
+ xtype: 'toolbar'
+ ,dock: 'top'
+ ,style: {background: '#30BBF3'}
+ ,items: [
+ '->'
+ ,{
+ xtype: 'label'
+ ,html: 'Rambox CE needs permissions to use Microphone and Camera for the apps.'
+ }
+ ,{
+ xtype: 'button'
+ ,text: 'Grant permissions'
+ ,ui: 'decline'
+ ,handler: async function(btn) {
+ ipc.send('grantPermissions');
+ Ext.cq1('app-main').removeDocked(btn.up('toolbar'), true);
+ }
+ }
+ ,{
+ xtype: 'button'
+ ,text: 'Never ask again'
+ ,ui: 'decline'
+ ,handler: function(btn) {
+ Ext.cq1('app-main').removeDocked(btn.up('toolbar'), true);
+ localStorage.setItem('hideMacPermissions', true);
+ }
+ }
+ ,'->'
+ ,{
+ glyph: 'xf00d@FontAwesome'
+ ,baseCls: ''
+ ,style: 'cursor:pointer;'
+ ,handler: function(btn) { Ext.cq1('app-main').removeDocked(btn.up('toolbar'), true); }
+ }
+ ]
+ });
+ }
+
+
Ext.getStore('ServicesList').load(function (records, operations, success) {
if (!success) {
diff --git a/app/store/ServicesList.js b/app/store/ServicesList.js
index 2a96cd5a..59d42328 100644
--- a/app/store/ServicesList.js
+++ b/app/store/ServicesList.js
@@ -10,7 +10,7 @@ Ext.define('Rambox.store.ServicesList', {
,proxy: {
type: 'ajax',
- url: 'https://us-central1-rambox-d1326.cloudfunctions.net/ceApps',
+ url: 'https://raw.githubusercontent.com/saenzramiro/rambox/gh-pages/api/services.json',
reader: {
type: 'json',
rootProperty: 'responseText'
diff --git a/app/ux/WebView.js b/app/ux/WebView.js
index 3e627164..f4848dd6 100644
--- a/app/ux/WebView.js
+++ b/app/ux/WebView.js
@@ -238,6 +238,7 @@ Ext.define('Rambox.ux.WebView',{
return {
xtype: 'statusbar'
+ ,id: me.id+'statusbar'
,hidden: !me.record.get('statusbar')
,keep: me.record.get('statusbar')
,y: floating ? '-18px' : 'auto'
@@ -275,7 +276,8 @@ Ext.define('Rambox.ux.WebView',{
if ( !me.record.get('enabled') ) return;
var webview = me.getWebView();
- let googleLoginURLs = ['accounts.google.com/signin/oauth', 'accounts.google.com/ServiceLogin']
+ let googleLoginURLs = ['accounts.google.com/signin', 'accounts.google.com/ServiceLogin', ]
+ me.errorCodeLog = []
// Google Analytics Event
ga_storage._trackEvent('Services', 'load', me.type, 1, true);
@@ -321,6 +323,60 @@ Ext.define('Rambox.ux.WebView',{
me.onSearchText(e.result)
});
+ // On search text
+ webview.addEventListener('did-fail-load', function(e) {
+ console.info('The service fail at loading', me.src, e);
+ me.errorCodeLog.push(e.errorCode)
+
+ var attempt = me.errorCodeLog.filter(function(code) { return code === e.errorCode });
+
+ // Error codes: https://cs.chromium.org/chromium/src/net/base/net_error_list.h
+ var msg = []
+ msg[-2] = 'NET error: failed.'
+ msg[-3] = 'An operation was aborted (due to user action)'
+ msg[-7] = 'Connection timeout.'
+ msg[-21] = 'Network change.'
+ msg[-100] = 'The connection was reset. Check your internet connection.'
+ msg[-101] = 'The connection was reset. Check your internet connection.'
+ msg[-105] = 'Name not resolved. Check your internet connection.'
+ msg[-106] = 'There is no active internet connection.'
+ msg[-118] = 'Connection timed out. Check your internet connection.'
+ msg[-130] = 'Proxy connection failed. Please, check the proxy configuration.'
+ msg[-300] = 'The URL is invalid.'
+ msg[-324] = 'Empty response. Check your internet connection.'
+
+ switch ( e.errorCode ) {
+ case 0:
+ break
+ case -3: // An operation was aborted (due to user action) I think that gmail an other pages that use iframes stop some of them making this error fired
+ if ( attempt.length <= 4 ) return
+ setTimeout(() => me.reloadService(me), 200);
+ me.errorCodeLog = []
+ break;
+ case -2:
+ case -7:
+ case -21:
+ case -118:
+ case -324:
+ case -100:
+ case -101:
+ case -105:
+ attempt.length > 4 ? me.onFailLoad(msg[e.errorCode]) : setTimeout(() => me.reloadService(me), 2000);
+ break;
+ case -106:
+ me.onFailLoad(msg[e.errorCode])
+ break;
+ case -130:
+ // Could not create a connection to the proxy server. An error occurred
+ // either in resolving its name, or in connecting a socket to it.
+ // Note that this does NOT include failures during the actual "CONNECT" method
+ // of an HTTP proxy.
+ case -300:
+ attempt.length > 4 ? me.onFailLoad(msg[e.errorCode]) : me.reloadService(me);
+ break;
+ }
+ });
+
// Open links in default browser
webview.addEventListener('new-window', function(e) {
switch ( me.type ) {
@@ -691,6 +747,12 @@ Ext.define('Rambox.ux.WebView',{
}
}
+ ,onFailLoad: function(v) {
+ let me = this
+ me.errorCodeLog = []
+ setTimeout(() => Ext.getCmp(me.id+'statusbar').setStatus({ text: ' The service failed at loading, Error: '+ v }), 1000);
+ }
+
,showSearchBox: function(v) {
var me = this;
if ( !me.record.get('enabled') ) return;
diff --git a/app/view/add/Add.js b/app/view/add/Add.js
index c26af425..5cddec02 100644
--- a/app/view/add/Add.js
+++ b/app/view/add/Add.js
@@ -68,7 +68,7 @@ Ext.define('Rambox.view.add.Add',{
,emptyText: me.record.get('url') === '___' ? 'https://' : ''
,validator: function(v) {
if ( !me.edit ? me.record.get('url') !== '___' : me.service.get('url').indexOf('https://___') === 0 ) return true
- if ( v.match(/^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i) === null && v.match(/^http:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[.\w]*)*$/) === null ) return false;
+ if ( v.match(/^(?:(?:https?|ftp):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?$/i) === null && v.match(/^https:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[.\w]*)*$/) === null && v.match(/^http:\/\/\w+(\.\w+)*(:[0-9]+)?\/?(\/[.\w]*)*$/) === null ) return false;
return true;
}
,listeners: {
diff --git a/electron/menu.js b/electron/menu.js
index 4a5729a4..a49c9882 100644
--- a/electron/menu.js
+++ b/electron/menu.js
@@ -1,6 +1,7 @@
'use strict';
const os = require('os');
const electron = require('electron');
+const { systemPreferences } = require('electron')
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const shell = electron.shell;
@@ -229,7 +230,7 @@ module.exports = function(config) {
}
];
- if (process.platform === 'darwin') {
+ if ( process.platform === 'darwin' ) {
tpl.unshift({
label: appName,
submenu: [
@@ -288,6 +289,18 @@ module.exports = function(config) {
}
]
});
+ helpSubmenu.push({
+ type: 'separator'
+ });
+ helpSubmenu.push({
+ label: 'Grant Microphone and Camera permissions',
+ visible: systemPreferences.getMediaAccessStatus('microphone') !== 'granted' || systemPreferences.getMediaAccessStatus('camera') !== 'granted',
+ click(item, win) {
+ const webContents = win.webContents;
+ const send = webContents.send.bind(win.webContents);
+ send('grantPermissions');
+ }
+ });
} else {
tpl.unshift({
label: '&'+locale['menu.file[0]'],
diff --git a/package-lock.json b/package-lock.json
index 062f358c..89575469 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -843,7 +843,8 @@
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.1.tgz",
"integrity": "sha512-HRZPIjPcbwAVQvOTxR4YE3o8Xs98NqbbL1iEZDCz7CL8ql0Lt5iOyJFxfnAB0oFs8Oh02F/lLlg30Mexv46LjA==",
- "dev": true
+ "dev": true,
+ "optional": true
},
"boxen": {
"version": "3.2.0",
@@ -2280,6 +2281,59 @@
"resolved": "https://registry.npmjs.org/electron-log/-/electron-log-2.2.17.tgz",
"integrity": "sha512-v+Af5W5z99ehhaLOfE9eTSXUwjzh2wFlQjz51dvkZ6ZIrET6OB/zAZPvsuwT6tm3t5x+M1r+Ed3U3xtPZYAyuQ=="
},
+ "electron-notarize": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/electron-notarize/-/electron-notarize-0.2.1.tgz",
+ "integrity": "sha512-oZ6/NhKeXmEKNROiFmRNfytqu3cxqC95sjooG7kBXQVEUSQkZnbiAhxVh5jXngL881G197pbwpeVPJyM7Ikmxw==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "fs-extra": "^8.1.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ }
+ },
+ "graceful-fs": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
+ "integrity": "sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==",
+ "dev": true
+ },
+ "jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
+ "dev": true,
+ "requires": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ }
+ }
+ },
"electron-osx-sign": {
"version": "0.4.10",
"resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.10.tgz",
@@ -3613,7 +3667,7 @@
"dependencies": {
"minimist": {
"version": "1.2.0",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+ "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
"dev": true
}
diff --git a/package.json b/package.json
index 830a0ab3..eab3e858 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "Rambox",
"productName": "Rambox",
- "version": "0.7.3",
+ "version": "0.7.4",
"description": "Free and Open Source messaging and emailing app that combines common web applications into one.",
"main": "electron/main.js",
"repository": {
@@ -66,18 +66,27 @@
"appId": "com.grupovrs.ramboxce",
"asar": true,
"electronDownload": {
- "version": "7.1.1"
+ "version": "7.1.12"
},
"mac": {
"category": "public.app-category.productivity",
"artifactName": "Rambox-${version}-mac.${ext}",
"target": [
"default"
- ]
+ ],
+ "hardenedRuntime": true,
+ "gatekeeperAssess": false,
+ "entitlements": "resources/installer/entitlements.mac.plist",
+ "entitlementsInherit": "resources/installer/entitlements.mac.plist",
+ "extendInfo": {
+ "NSMicrophoneUsageDescription": "Apps inside Rambox CE may need access to your microphone. Please, grant access to have a better experience.",
+ "NSCameraUsageDescription": "Apps inside Rambox CE may need access to your camera. Please, grant access to have a better experience."
+ }
},
"dmg": {
"title": "Rambox",
"iconSize": 128,
+ "sign": false,
"contents": [
{
"x": 355,
@@ -189,6 +198,7 @@
"csvjson": "4.3.3",
"electron": "7.1.12",
"electron-builder": "21.2.0",
+ "electron-notarize": "0.2.1",
"electron-packager": "^12.1.0",
"mocha": "^5.2.0",
"spectron": "^3.8.0"
diff --git a/resources/installer/entitlements.mac.plist b/resources/installer/entitlements.mac.plist
new file mode 100644
index 00000000..7d10056a
--- /dev/null
+++ b/resources/installer/entitlements.mac.plist
@@ -0,0 +1,14 @@
+
+
+
+
+ com.apple.security.cs.allow-unsigned-executable-memory
+
+ com.apple.security.device.microphone
+
+ com.apple.security.device.camera
+
+ com.apple.security.device.audio-input
+
+
+
diff --git a/resources/installer/notarize.js b/resources/installer/notarize.js
new file mode 100644
index 00000000..5d2b5562
--- /dev/null
+++ b/resources/installer/notarize.js
@@ -0,0 +1,17 @@
+const { notarize } = require('electron-notarize');
+
+exports.default = async function notarizing(context) {
+ const { electronPlatformName, appOutDir } = context;
+ if (electronPlatformName !== 'darwin') {
+ return;
+ }
+
+ const appName = context.packager.appInfo.productFilename;
+
+ return await notarize({
+ appBundleId: 'com.grupovrs.ramboxce',
+ appPath: `${appOutDir}/${appName}.app`,
+ appleId: 'saenzramiro@gmail.com',
+ appleIdPassword: process.env.APPLE_ID_PWD
+ });
+};
\ No newline at end of file