mirror of
https://github.com/finos/SymphonyElectron.git
synced 2025-02-25 18:55:29 -06:00
parent
9785baacc3
commit
439f283916
@ -135,10 +135,10 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
var win;
|
var win;
|
||||||
|
|
||||||
var openWinButton = document.getElementById('open-win');
|
var openWinButton = document.getElementById('open-win');
|
||||||
openWinButton.addEventListener('click', function() {
|
openWinButton.addEventListener('click', function() {
|
||||||
console.log('win=', win)
|
win = window.open('win.html?x=100&y=100', 'test-window', 'height=100,width=100');
|
||||||
win = window.open('win.html', 'test-window', 'height=100,width=100');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
var front = document.getElementById('bring-to-front');
|
var front = document.getElementById('bring-to-front');
|
||||||
@ -146,5 +146,12 @@
|
|||||||
window.SYM_API.activate(win.name);
|
window.SYM_API.activate(win.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// register callback to be notified when size/position changes for win.
|
||||||
|
SYM_API.registerBoundsChange(onBoundsChange);
|
||||||
|
|
||||||
|
function onBoundsChange(arg) {
|
||||||
|
console.log('bounds changed for=', arg)
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</html>
|
</html>
|
||||||
|
167
js/config.js
Normal file
167
js/config.js
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
const electron = require('electron');
|
||||||
|
const app = electron.app;
|
||||||
|
const path = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
const isDevEnv = require('./utils/misc.js').isDevEnv;
|
||||||
|
const isMac = require('./utils/misc.js').isMac;
|
||||||
|
const getRegistry = require('./utils/getRegistry.js');
|
||||||
|
const configFileName = 'Symphony.config';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to read given field from user config file, if field doesn't exist
|
||||||
|
* then tries reading from global config. User config is stord in directory:
|
||||||
|
* app.getPath('userData') and file called Symphony.config. Global config is
|
||||||
|
* stored in file Symphony.config in directory where executable gets installed.
|
||||||
|
*
|
||||||
|
* Config is a flat key/value file.
|
||||||
|
* e.g. { url: 'https://my.symphony.com', }
|
||||||
|
*
|
||||||
|
* @param {String} fieldName Name of field to try fetching
|
||||||
|
* @return {Promise} Returns promise that will succeed with field
|
||||||
|
* value if found in either user or global config. Otherwise will fail promise.
|
||||||
|
*/
|
||||||
|
function getConfigField(fieldName) {
|
||||||
|
return getUserConfigField(fieldName)
|
||||||
|
.then(function(value) {
|
||||||
|
// got value from user config
|
||||||
|
return value;
|
||||||
|
}, function () {
|
||||||
|
// failed to get value from user config, so try global config
|
||||||
|
return getGlobalConfigField(fieldName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getUserConfigField(fieldName) {
|
||||||
|
return readUserConfig().then(function(config) {
|
||||||
|
if (typeof fieldName === 'string' && fieldName in config) {
|
||||||
|
return config[fieldName];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('field does not exist in user config: ' + fieldName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function readUserConfig() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
let configPath = path.join(app.getPath('userData'), configFileName);
|
||||||
|
|
||||||
|
fs.readFile(configPath, 'utf8', function(err, data) {
|
||||||
|
if (err) {
|
||||||
|
reject('cannot open user config file: ' + configPath + ', error: ' + err);
|
||||||
|
} else {
|
||||||
|
let config = {};
|
||||||
|
try {
|
||||||
|
// data is the contents of the text file we just read
|
||||||
|
config = JSON.parse(data);
|
||||||
|
} catch (e) {
|
||||||
|
reject('can not parse user config file data: ' + data + ', error: ' + err);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve(config);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGlobalConfigField(fieldName) {
|
||||||
|
return readGlobalConfig().then(function(config) {
|
||||||
|
if (typeof fieldName === 'string' && fieldName in config) {
|
||||||
|
return config[fieldName];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('field does not exist in global config: ' + fieldName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reads global configuration file: config/Symphony.config. this file is
|
||||||
|
* hold items (such as the start url) that are intended to be used as
|
||||||
|
* global (or default) values for all users running this app. for production
|
||||||
|
* this file is located relative to the executable - it is placed there by
|
||||||
|
* the installer. this makes the file easily modifable by admin (or person who
|
||||||
|
* installed app). for dev env, the file is read directly from packed asar file.
|
||||||
|
*/
|
||||||
|
function readGlobalConfig() {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
let configPath;
|
||||||
|
let globalConfigFileName = path.join('config', configFileName);
|
||||||
|
if (isDevEnv) {
|
||||||
|
// for dev env, get config file from asar
|
||||||
|
configPath = path.join(app.getAppPath(), globalConfigFileName);
|
||||||
|
} else {
|
||||||
|
// for non-dev, config file is placed by installer relative to exe.
|
||||||
|
// this is so the config can be easily be changed post install.
|
||||||
|
let execPath = path.dirname(app.getPath('exe'));
|
||||||
|
// for mac exec is stored in subdir, for linux/windows config
|
||||||
|
// dir is in the same location.
|
||||||
|
configPath = path.join(execPath, isMac ? '..' : '', globalConfigFileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.readFile(configPath, 'utf8', function(err, data) {
|
||||||
|
if (err) {
|
||||||
|
reject('cannot open global config file: ' + configPath + ', error: ' + err);
|
||||||
|
} else {
|
||||||
|
let config = {};
|
||||||
|
try {
|
||||||
|
// data is the contents of the text file we just read
|
||||||
|
config = JSON.parse(data);
|
||||||
|
} catch (e) {
|
||||||
|
reject('can not parse config file data: ' + data + ', error: ' + err);
|
||||||
|
}
|
||||||
|
getRegistry('PodUrl')
|
||||||
|
.then(function(url){
|
||||||
|
config.url = url;
|
||||||
|
resolve(config);
|
||||||
|
}).catch(function (){
|
||||||
|
resolve(config);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates user config with given field with new value
|
||||||
|
* @param {String} fieldName [description]
|
||||||
|
* @param {Object} newValue object to replace given value
|
||||||
|
* @return {[type]} [description]
|
||||||
|
*/
|
||||||
|
function updateConfigField(fieldName, newValue) {
|
||||||
|
return readUserConfig()
|
||||||
|
.then(function(config) {
|
||||||
|
return saveUserConfig(fieldName, newValue, config);
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
// in case config doesn't exist, can't read or is corrupted.
|
||||||
|
return saveUserConfig(fieldName, newValue, {});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveUserConfig(fieldName, newValue, oldConfig) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
let configPath = path.join(app.getPath('userData'), configFileName);
|
||||||
|
|
||||||
|
if (!oldConfig || !fieldName) {
|
||||||
|
reject('can not save config, invalid input');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clone and set new value
|
||||||
|
let newConfig = Object.assign({}, oldConfig);
|
||||||
|
newConfig[fieldName] = newValue;
|
||||||
|
|
||||||
|
let jsonNewConfig = JSON.stringify(newConfig, null, ' ');
|
||||||
|
|
||||||
|
fs.writeFile(configPath, jsonNewConfig, 'utf8', (err) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { getConfigField, updateConfigField };
|
@ -7,7 +7,8 @@ const cmds = keyMirror({
|
|||||||
registerLogger: null,
|
registerLogger: null,
|
||||||
setBadgeCount: null,
|
setBadgeCount: null,
|
||||||
badgeDataUrl: null,
|
badgeDataUrl: null,
|
||||||
activate: null
|
activate: null,
|
||||||
|
registerBoundsChange: null
|
||||||
});
|
});
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
@ -1,59 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
|
|
||||||
const electron = require('electron');
|
|
||||||
const app = electron.app;
|
|
||||||
const path = require('path');
|
|
||||||
const fs = require('fs');
|
|
||||||
const isDevEnv = require('./utils/misc.js').isDevEnv;
|
|
||||||
const isMac = require('./utils/misc.js').isMac;
|
|
||||||
const getRegistry = require('./utils/getRegistry.js');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* reads global configuration file: config/Symphony.config. this file is
|
|
||||||
* hold items (such as the start url) that are intended to be used as
|
|
||||||
* global (or default) values for all users running this app. for production
|
|
||||||
* this file is located relative to the executable - it is placed there by
|
|
||||||
* the installer. this makes the file easily modifable by admin (or person who
|
|
||||||
* installed app). for dev env, the file is read directly from packed asar file.
|
|
||||||
*/
|
|
||||||
var getConfig = function () {
|
|
||||||
var promise = new Promise(function(resolve, reject) {
|
|
||||||
let configPath;
|
|
||||||
const configFile = 'config/Symphony.config';
|
|
||||||
if (isDevEnv) {
|
|
||||||
// for dev env, get config file from asar
|
|
||||||
configPath = path.join(app.getAppPath(), configFile);
|
|
||||||
} else {
|
|
||||||
// for non-dev, config file is placed by installer relative to exe.
|
|
||||||
// this is so the config can be easily be changed post install.
|
|
||||||
let execPath = path.dirname(app.getPath('exe'));
|
|
||||||
// for mac exec is stored in subdir, for linux/windows config
|
|
||||||
// dir is in the same location.
|
|
||||||
configPath = path.join(execPath, isMac ? '..' : '', configFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
fs.readFile(configPath, 'utf8', function(err, data) {
|
|
||||||
if (err) {
|
|
||||||
reject('cannot open config file: ' + configPath + ', error: ' + err);
|
|
||||||
} else {
|
|
||||||
let config = {};
|
|
||||||
try {
|
|
||||||
// data is the contents of the text file we just read
|
|
||||||
config = JSON.parse(data);
|
|
||||||
} catch (e) {
|
|
||||||
reject('can not parse config file data: ' + data + ', error: ' + err);
|
|
||||||
}
|
|
||||||
getRegistry('PodUrl')
|
|
||||||
.then(function(url){
|
|
||||||
config.url = url;
|
|
||||||
resolve(config);
|
|
||||||
}).catch(function (){
|
|
||||||
resolve(config);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = getConfig
|
|
@ -5,7 +5,7 @@ const app = electron.app;
|
|||||||
const nodeURL = require('url');
|
const nodeURL = require('url');
|
||||||
const squirrelStartup = require('electron-squirrel-startup');
|
const squirrelStartup = require('electron-squirrel-startup');
|
||||||
|
|
||||||
const getConfig = require('./getConfig.js');
|
const { getConfigField } = require('./config.js');
|
||||||
const { isMac, isDevEnv } = require('./utils/misc.js');
|
const { isMac, isDevEnv } = require('./utils/misc.js');
|
||||||
|
|
||||||
|
|
||||||
@ -44,17 +44,17 @@ function getUrlAndOpenMainWindow() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getConfig()
|
getConfigField('url')
|
||||||
.then(createWin).catch(function (err){
|
.then(createWin).catch(function (err){
|
||||||
let title = 'Error loading configuration';
|
let title = 'Error loading configuration';
|
||||||
electron.dialog.showErrorBox(title, title + ': ' + err);
|
electron.dialog.showErrorBox(title, title + ': ' + err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function createWin(config){
|
function createWin(urlFromConfig){
|
||||||
let protocol = '';
|
let protocol = '';
|
||||||
// add https protocol if none found.
|
// add https protocol if none found.
|
||||||
let parsedUrl = nodeURL.parse(config.url);
|
let parsedUrl = nodeURL.parse(urlFromConfig);
|
||||||
if (!parsedUrl.protocol) {
|
if (!parsedUrl.protocol) {
|
||||||
protocol = 'https';
|
protocol = 'https';
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,10 @@ electron.ipcMain.on(apiName, (event, arg) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (arg.cmd === apiCmds.registerBoundsChange) {
|
||||||
|
windowMgr.setBoundsChangeWindow(event.sender);
|
||||||
|
}
|
||||||
|
|
||||||
if (arg.cmd === apiCmds.registerLogger) {
|
if (arg.cmd === apiCmds.registerLogger) {
|
||||||
// renderer window that has a registered logger from JS.
|
// renderer window that has a registered logger from JS.
|
||||||
log.setLogWindow(event.sender);
|
log.setLogWindow(event.sender);
|
||||||
|
@ -87,6 +87,22 @@ function createAPI() {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows JS to register a callback to be invoked when size/positions
|
||||||
|
* changes for any pop-out window (i.e., window.open). The main
|
||||||
|
* process will emit IPC event 'boundsChange' (see below). Currently
|
||||||
|
* only one window can register for bounds change.
|
||||||
|
* @param {Function} callback Function invoked when bounds changes.
|
||||||
|
*/
|
||||||
|
registerBoundsChange: function(callback) {
|
||||||
|
if (typeof callback === 'function') {
|
||||||
|
local.boundsChangeCallback = callback;
|
||||||
|
local.ipcRenderer.send(apiName, {
|
||||||
|
cmd: apiCmds.registerBoundsChange
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* allows JS to register a logger that can be used by electron main process.
|
* allows JS to register a logger that can be used by electron main process.
|
||||||
* @param {Object} logger function that can be called accepting
|
* @param {Object} logger function that can be called accepting
|
||||||
@ -94,9 +110,6 @@ function createAPI() {
|
|||||||
* logLevel: 'ERROR'|'CONFLICT'|'WARN'|'ACTION'|'INFO'|'DEBUG',
|
* logLevel: 'ERROR'|'CONFLICT'|'WARN'|'ACTION'|'INFO'|'DEBUG',
|
||||||
* logDetails: String
|
* logDetails: String
|
||||||
* }
|
* }
|
||||||
*
|
|
||||||
* note: only main window is allowed to register a logger, others are
|
|
||||||
* ignored.
|
|
||||||
*/
|
*/
|
||||||
registerLogger: function(logger) {
|
registerLogger: function(logger) {
|
||||||
if (typeof logger === 'function') {
|
if (typeof logger === 'function') {
|
||||||
@ -119,6 +132,20 @@ function createAPI() {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// listen for notifications that some window size/position has changed
|
||||||
|
local.ipcRenderer.on('boundsChange', (event, arg) => {
|
||||||
|
if (local.boundsChangeCallback && arg.windowName &&
|
||||||
|
arg.x && arg.y && arg.width && arg.height) {
|
||||||
|
local.boundsChangeCallback({
|
||||||
|
x: arg.x,
|
||||||
|
y: arg.y,
|
||||||
|
width: arg.width,
|
||||||
|
height: arg.height,
|
||||||
|
windowName: arg.windowName
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use render process to create badge count img and send back to main process.
|
* Use render process to create badge count img and send back to main process.
|
||||||
* If number is greater than 99 then 99+ img is returned.
|
* If number is greater than 99 then 99+ img is returned.
|
||||||
|
29
js/utils/isInDisplayBounds.js
Normal file
29
js/utils/isInDisplayBounds.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
|
const electron = require('electron');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if given rectangle is contained within the workArea of at
|
||||||
|
* least one of the screens.
|
||||||
|
* @param {x: Number, y: Number, width: Number, height: Number} rect
|
||||||
|
* @return {Boolean} true if condition in desc is met.
|
||||||
|
*/
|
||||||
|
function isInDisplayBounds(rect) {
|
||||||
|
if (!rect) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let displays = electron.screen.getAllDisplays();
|
||||||
|
|
||||||
|
for(let i = 0, len = displays.length; i < len; i++) {
|
||||||
|
let workArea = displays[i].workArea;
|
||||||
|
if (rect.x >= workArea.x && rect.y >= workArea.y &&
|
||||||
|
((rect.x + rect.width) <= (workArea.x + workArea.width)) &&
|
||||||
|
((rect.y + rect.height) <= (workArea.y + workArea.height))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = isInDisplayBounds;
|
@ -18,7 +18,7 @@ function throttle(throttleTime, func) {
|
|||||||
|
|
||||||
function cancel() {
|
function cancel() {
|
||||||
if (timer) {
|
if (timer) {
|
||||||
window.clearTimeout(timer);
|
clearTimeout(timer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
204
js/windowMgr.js
204
js/windowMgr.js
@ -4,14 +4,18 @@ const electron = require('electron');
|
|||||||
const app = electron.app;
|
const app = electron.app;
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const nodeURL = require('url');
|
const nodeURL = require('url');
|
||||||
|
const querystring = require('querystring');
|
||||||
|
|
||||||
const menuTemplate = require('./menus/menuTemplate.js');
|
const menuTemplate = require('./menus/menuTemplate.js');
|
||||||
const loadErrors = require('./dialogs/showLoadError.js');
|
const loadErrors = require('./dialogs/showLoadError.js');
|
||||||
const { isMac } = require('./utils/misc.js');
|
const { isMac } = require('./utils/misc.js');
|
||||||
|
const isInDisplayBounds = require('./utils/isInDisplayBounds.js');
|
||||||
const getGuid = require('./utils/getGuid.js');
|
const getGuid = require('./utils/getGuid.js');
|
||||||
const log = require('./log.js')
|
const log = require('./log.js')
|
||||||
const logLevels = require('./enums/logLevels.js');
|
const logLevels = require('./enums/logLevels.js');
|
||||||
const notify = require('./notify/electron-notify.js');
|
const notify = require('./notify/electron-notify.js');
|
||||||
|
const throttle = require('./utils/throttle.js');
|
||||||
|
const { getConfigField, updateConfigField } = require('./config.js');
|
||||||
|
|
||||||
//context menu
|
//context menu
|
||||||
const contextMenu = require('./menus/contextMenu.js');
|
const contextMenu = require('./menus/contextMenu.js');
|
||||||
@ -25,6 +29,7 @@ let mainWindow;
|
|||||||
let windows = {};
|
let windows = {};
|
||||||
let willQuitApp = false;
|
let willQuitApp = false;
|
||||||
let isOnline = true;
|
let isOnline = true;
|
||||||
|
let boundsChangeWindow;
|
||||||
|
|
||||||
// note: this file is built using browserify in prebuild step.
|
// note: this file is built using browserify in prebuild step.
|
||||||
const preloadMainScript = path.join(__dirname, 'preload/_preloadMain.js');
|
const preloadMainScript = path.join(__dirname, 'preload/_preloadMain.js');
|
||||||
@ -37,19 +42,29 @@ function removeWindowKey(key) {
|
|||||||
delete windows[ key ];
|
delete windows[ key ];
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHostFromUrl(url) {
|
function getParsedUrl(url) {
|
||||||
let parsedUrl = nodeURL.parse(url);
|
let parsedUrl = nodeURL.parse(url);
|
||||||
let UrlHost = parsedUrl.host;
|
return parsedUrl;
|
||||||
return UrlHost
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMainWindow(initialUrl) {
|
function createMainWindow(initialUrl) {
|
||||||
|
getConfigField('mainWinPos').then(
|
||||||
|
function(bounds) {
|
||||||
|
doCreateMainWindow(initialUrl, bounds);
|
||||||
|
},
|
||||||
|
function() {
|
||||||
|
// failed, use default bounds
|
||||||
|
doCreateMainWindow(initialUrl, null);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function doCreateMainWindow(initialUrl, initialBounds) {
|
||||||
let url = initialUrl;
|
let url = initialUrl;
|
||||||
let key = getGuid();
|
let key = getGuid();
|
||||||
|
|
||||||
let newWinOpts = {
|
let newWinOpts = {
|
||||||
title: 'Symphony',
|
title: 'Symphony',
|
||||||
width: 1024, height: 768,
|
|
||||||
show: true,
|
show: true,
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
sandbox: true,
|
sandbox: true,
|
||||||
@ -59,12 +74,39 @@ function createMainWindow(initialUrl) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// set size and postion
|
||||||
|
let bounds = initialBounds;
|
||||||
|
|
||||||
|
// if bounds if not fully contained in some display then use default size
|
||||||
|
// and position.
|
||||||
|
if (!isInDisplayBounds(bounds)) {
|
||||||
|
bounds = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bounds && bounds.width && bounds.height) {
|
||||||
|
newWinOpts.width = bounds.width;
|
||||||
|
newWinOpts.height = bounds.height;
|
||||||
|
} else {
|
||||||
|
newWinOpts.width = 1024;
|
||||||
|
newWinOpts.height = 768;
|
||||||
|
}
|
||||||
|
|
||||||
|
// will center on screen if values not provided
|
||||||
|
if (bounds && bounds.x && bounds.y) {
|
||||||
|
newWinOpts.x = bounds.x;
|
||||||
|
newWinOpts.y = bounds.y;
|
||||||
|
}
|
||||||
|
|
||||||
// note: augmenting with some custom values
|
// note: augmenting with some custom values
|
||||||
newWinOpts.winKey = key;
|
newWinOpts.winKey = key;
|
||||||
|
|
||||||
mainWindow = new electron.BrowserWindow(newWinOpts);
|
mainWindow = new electron.BrowserWindow(newWinOpts);
|
||||||
mainWindow.winName = 'main';
|
mainWindow.winName = 'main';
|
||||||
|
|
||||||
|
let throttledMainWinBoundsChange = throttle(5000, saveMainWinBounds);
|
||||||
|
mainWindow.on('move', throttledMainWinBoundsChange);
|
||||||
|
mainWindow.on('resize',throttledMainWinBoundsChange);
|
||||||
|
|
||||||
function retry() {
|
function retry() {
|
||||||
if (!isOnline) {
|
if (!isOnline) {
|
||||||
loadErrors.showNetworkConnectivityError(mainWindow, url, retry);
|
loadErrors.showNetworkConnectivityError(mainWindow, url, retry);
|
||||||
@ -128,8 +170,11 @@ function createMainWindow(initialUrl) {
|
|||||||
// open external links in default browser - a tag, window.open
|
// open external links in default browser - a tag, window.open
|
||||||
mainWindow.webContents.on('new-window', function(event, newWinUrl,
|
mainWindow.webContents.on('new-window', function(event, newWinUrl,
|
||||||
frameName, disposition, newWinOptions) {
|
frameName, disposition, newWinOptions) {
|
||||||
let newWinHost = getHostFromUrl(newWinUrl);
|
let newWinParsedUrl = getParsedUrl(newWinUrl);
|
||||||
let mainWinHost = getHostFromUrl(url);
|
let mainWinParsedUrl = getParsedUrl(url);
|
||||||
|
|
||||||
|
let newWinHost = newWinParsedUrl && newWinParsedUrl.host;
|
||||||
|
let mainWinHost = mainWinParsedUrl && mainWinParsedUrl.host;
|
||||||
|
|
||||||
// if host url doesn't match then open in external browser
|
// if host url doesn't match then open in external browser
|
||||||
if (newWinHost !== mainWinHost) {
|
if (newWinHost !== mainWinHost) {
|
||||||
@ -144,44 +189,70 @@ function createMainWindow(initialUrl) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reposition new window
|
let x = 0;
|
||||||
let mainWinPos = mainWindow.getPosition();
|
let y = 0;
|
||||||
if (mainWinPos && mainWinPos.length === 2) {
|
|
||||||
let newWinKey = getGuid();
|
|
||||||
|
|
||||||
/* eslint-disable no-param-reassign */
|
let width = newWinOptions.width || 300;
|
||||||
newWinOptions.x = mainWinPos[0] + 50;
|
let height = newWinOptions.height || 600;
|
||||||
newWinOptions.y = mainWinPos[1] + 50;
|
|
||||||
|
|
||||||
newWinOptions.winKey = newWinKey;
|
// try getting x and y position from query parameters
|
||||||
/* eslint-enable no-param-reassign */
|
var query = newWinParsedUrl && querystring.parse(newWinParsedUrl.query);
|
||||||
|
if (query && query.x && query.y) {
|
||||||
|
let newX = Number.parseInt(query.x, 10);
|
||||||
|
let newY = Number.parseInt(query.y, 10);
|
||||||
|
|
||||||
let webContents = newWinOptions.webContents;
|
let newWinRect = { x: newX, y: newY, width, height };
|
||||||
|
|
||||||
webContents.once('did-finish-load', function() {
|
// only accept if both are successfully parsed.
|
||||||
let browserWin = electron.BrowserWindow.fromWebContents(webContents);
|
if (Number.isInteger(newX) && Number.isInteger(newY) &&
|
||||||
|
isInDisplayBounds(newWinRect)) {
|
||||||
if (browserWin) {
|
x = newX;
|
||||||
browserWin.winName = frameName;
|
y = newY;
|
||||||
|
} else {
|
||||||
browserWin.once('closed', function() {
|
x = 0;
|
||||||
removeWindowKey(newWinKey);
|
y = 0;
|
||||||
});
|
}
|
||||||
|
} else {
|
||||||
addWindowKey(newWinKey, browserWin);
|
// create new window at slight offset from main window.
|
||||||
}
|
({ x, y } = getWindowSizeAndPosition(mainWindow));
|
||||||
|
x+=50;
|
||||||
// note: will use later for save-layout feature
|
y+=50;
|
||||||
// browserWin.on('move', function() {
|
|
||||||
// var newPos = browserWin.getPosition();
|
|
||||||
// console.log('new pos=', newPos)
|
|
||||||
// });
|
|
||||||
// browserWin.on('resize', function() {
|
|
||||||
// var newSize = browserWin.getSize();
|
|
||||||
// console.log('new size=', newSize)
|
|
||||||
// });
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* eslint-disable no-param-reassign */
|
||||||
|
newWinOptions.x = x;
|
||||||
|
newWinOptions.y = y;
|
||||||
|
newWinOptions.width = width;
|
||||||
|
newWinOptions.height = height;
|
||||||
|
|
||||||
|
let newWinKey = getGuid();
|
||||||
|
|
||||||
|
newWinOptions.winKey = newWinKey;
|
||||||
|
/* eslint-enable no-param-reassign */
|
||||||
|
|
||||||
|
let webContents = newWinOptions.webContents;
|
||||||
|
|
||||||
|
webContents.once('did-finish-load', function() {
|
||||||
|
let browserWin = electron.BrowserWindow.fromWebContents(webContents);
|
||||||
|
|
||||||
|
if (browserWin) {
|
||||||
|
browserWin.winName = frameName;
|
||||||
|
|
||||||
|
browserWin.once('closed', function() {
|
||||||
|
removeWindowKey(newWinKey);
|
||||||
|
browserWin.removeListener('move', throttledBoundsChange);
|
||||||
|
browserWin.removeListener('resize', throttledBoundsChange);
|
||||||
|
});
|
||||||
|
|
||||||
|
addWindowKey(newWinKey, browserWin);
|
||||||
|
|
||||||
|
// throttle changes so we don't flood client.
|
||||||
|
let throttledBoundsChange = throttle(1000,
|
||||||
|
sendChildWinBoundsChange.bind(null, browserWin));
|
||||||
|
browserWin.on('move', throttledBoundsChange);
|
||||||
|
browserWin.on('resize',throttledBoundsChange);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -192,10 +263,35 @@ app.on('before-quit', function() {
|
|||||||
willQuitApp = true;
|
willQuitApp = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function saveMainWinBounds() {
|
||||||
|
let newBounds = getWindowSizeAndPosition(mainWindow);
|
||||||
|
|
||||||
|
if (newBounds) {
|
||||||
|
updateConfigField('mainWinPos', newBounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getMainWindow() {
|
function getMainWindow() {
|
||||||
return mainWindow;
|
return mainWindow;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getWindowSizeAndPosition(window) {
|
||||||
|
let newPos = window.getPosition();
|
||||||
|
let newSize = window.getSize();
|
||||||
|
|
||||||
|
if (newPos && newPos.length === 2 &&
|
||||||
|
newSize && newSize.length === 2 ) {
|
||||||
|
return {
|
||||||
|
x: newPos[0],
|
||||||
|
y: newPos[1],
|
||||||
|
width: newSize[0],
|
||||||
|
height: newSize[1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function showMainWindow() {
|
function showMainWindow() {
|
||||||
mainWindow.show();
|
mainWindow.show();
|
||||||
}
|
}
|
||||||
@ -217,6 +313,12 @@ function setIsOnline(status) {
|
|||||||
isOnline = status;
|
isOnline = status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries finding a window we have created with given name. If founds then
|
||||||
|
* brings to front and gives focus.
|
||||||
|
* @param {String} windowName Name of target window. Note: main window has
|
||||||
|
* name 'main'.
|
||||||
|
*/
|
||||||
function activate(windowName) {
|
function activate(windowName) {
|
||||||
let keys = Object.keys(windows);
|
let keys = Object.keys(windows);
|
||||||
for(let i = 0, len = keys.length; i < len; i++) {
|
for(let i = 0, len = keys.length; i < len; i++) {
|
||||||
@ -229,6 +331,27 @@ function activate(windowName) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* name of renderer window to notify when bounds of child window changes.
|
||||||
|
* @param {object} window Renderer window to use IPC with to inform about size/
|
||||||
|
* position change.
|
||||||
|
*/
|
||||||
|
function setBoundsChangeWindow(window) {
|
||||||
|
boundsChangeWindow = window;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when bounds of child window changes size/position
|
||||||
|
* @param {object} window Child window which has changed size/position.
|
||||||
|
*/
|
||||||
|
function sendChildWinBoundsChange(window) {
|
||||||
|
let newBounds = getWindowSizeAndPosition(window);
|
||||||
|
if (newBounds && boundsChangeWindow) {
|
||||||
|
newBounds.windowName = window.winName;
|
||||||
|
boundsChangeWindow.send('boundsChange', newBounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
createMainWindow: createMainWindow,
|
createMainWindow: createMainWindow,
|
||||||
getMainWindow: getMainWindow,
|
getMainWindow: getMainWindow,
|
||||||
@ -236,5 +359,6 @@ module.exports = {
|
|||||||
isMainWindow: isMainWindow,
|
isMainWindow: isMainWindow,
|
||||||
hasWindow: hasWindow,
|
hasWindow: hasWindow,
|
||||||
setIsOnline: setIsOnline,
|
setIsOnline: setIsOnline,
|
||||||
activate: activate
|
activate: activate,
|
||||||
|
setBoundsChangeWindow: setBoundsChangeWindow
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
const getConfig = require('../js/getconfig');
|
const { getConfigField } = require('../js/config');
|
||||||
|
|
||||||
// mock required so getConfig reads config from correct path
|
// mock required so getConfig reads config from correct path
|
||||||
jest.mock('../js/utils/misc.js', function() {
|
jest.mock('../js/utils/misc.js', function() {
|
||||||
@ -9,7 +9,7 @@ jest.mock('../js/utils/misc.js', function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('getConfig should have proper url', function() {
|
test('getConfig should have proper url', function() {
|
||||||
return getConfig(false).then(function(result) {
|
return getConfigField('url').then(function(url) {
|
||||||
expect(result.url).toBe('https://my.symphony.com');
|
expect(url).toBe('https://my.symphony.com');
|
||||||
});
|
});
|
||||||
});
|
});
|
Loading…
Reference in New Issue
Block a user