mirror of
https://github.com/finos/SymphonyElectron.git
synced 2026-07-29 23:57:56 -05:00
Electron-43 (Configure Notification Position) (#142)
* ELECTRON-43 - Implemented Alert settings functionality * ELECTRON-43 1. Added multiple monitor support for notifications in windows 2. Implemented a settings window to change notification position 3. Completed implementing configure alert position window * ELECTRON-43 - Refactored code and changed config data * ELECTRON-43 1. Refactored the code 2. Added modal property to browser window * ELECTRON-43 1. Resolved conflicts 2. Made config default display value to null 3. Renamed api method from 'showAlertSettings' to 'showNotificationSettings' for consistency 4. Fixed some bugs
This commit is contained in:
@@ -2,5 +2,10 @@
|
||||
"url": "https://foundation-dev.symphony.com",
|
||||
"minimizeOnClose" : false,
|
||||
"launchOnStartup" : true,
|
||||
"alwaysOnTop" : false
|
||||
"alwaysOnTop" : false,
|
||||
"launchOnStartup" : true,
|
||||
"notificationSettings": {
|
||||
"position": "upper-right",
|
||||
"display": ""
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@
|
||||
<label for='tag'>tag:</label>
|
||||
<input type='text' id='tag' value=''/>
|
||||
</p>
|
||||
<button id='open-config-win'>Open configure window</button>
|
||||
<br>
|
||||
<hr>
|
||||
<p>Badge Count:<p>
|
||||
@@ -66,6 +67,12 @@
|
||||
Version Info:<span id='info'></span>
|
||||
</body>
|
||||
<script>
|
||||
var openConfigWin = document.getElementById('open-config-win');
|
||||
|
||||
openConfigWin.addEventListener('click', function() {
|
||||
ssf.showNotificationSettings();
|
||||
});
|
||||
|
||||
var notfEl = document.getElementById('notf');
|
||||
var num = 0;
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ const cmds = keyMirror({
|
||||
registerBoundsChange: null,
|
||||
registerProtocolHandler: null,
|
||||
registerActivityDetection: null,
|
||||
showNotificationSettings: null,
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
|
||||
@@ -12,6 +12,7 @@ const logLevels = require('./enums/logLevels');
|
||||
const activityDetection = require('./activityDetection/activityDetection');
|
||||
const badgeCount = require('./badgeCount.js');
|
||||
const protocolHandler = require('./protocolHandler');
|
||||
const configureNotification = require('./notify/settings/configure-notification-position');
|
||||
|
||||
const apiEnums = require('./enums/api.js');
|
||||
const apiCmds = apiEnums.cmds;
|
||||
@@ -98,6 +99,10 @@ electron.ipcMain.on(apiName, (event, arg) => {
|
||||
// renderer window that has a registered activity detection from JS.
|
||||
activityDetection.setActivityWindow(arg.period, event.sender);
|
||||
}
|
||||
|
||||
if (arg.cmd === apiCmds.showNotificationSettings && typeof arg.windowName === 'string') {
|
||||
configureNotification.openConfigurationWindow(arg.windowName);
|
||||
}
|
||||
});
|
||||
|
||||
// expose these methods primarily for testing...
|
||||
|
||||
@@ -301,6 +301,15 @@ function setCheckboxValues(){
|
||||
log.send(logLevels.ERROR, 'MenuTemplate: error getting config field alwaysOnTop, error: ' + err);
|
||||
electron.dialog.showErrorBox(title, title + ': ' + err);
|
||||
});
|
||||
|
||||
getConfigField('notificationSettings').then(function(notfObject) {
|
||||
eventEmitter.emit('notificationSettings', notfObject);
|
||||
}).catch(function (err){
|
||||
let title = 'Error loading configuration';
|
||||
log.send(logLevels.ERROR, 'MenuTemplate: error getting config field notificationSettings, error: ' + err);
|
||||
electron.dialog.showErrorBox(title, title + ': ' + err);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function getMinimizeOnClose(){
|
||||
|
||||
@@ -15,6 +15,7 @@ const electron = require('electron');
|
||||
const app = electron.app;
|
||||
const BrowserWindow = electron.BrowserWindow;
|
||||
const ipc = electron.ipcMain;
|
||||
const { isMac } = require('../utils/misc');
|
||||
const log = require('../log.js');
|
||||
const logLevels = require('../enums/logLevels.js');
|
||||
|
||||
@@ -43,6 +44,9 @@ let closedNotifications = {};
|
||||
let latestID = 0;
|
||||
|
||||
let nextInsertPos = {};
|
||||
let externalDisplay;
|
||||
// user selected display id for notification
|
||||
let displayId;
|
||||
|
||||
let config = {
|
||||
// corner to put notifications
|
||||
@@ -145,6 +149,21 @@ if (app.isReady()) {
|
||||
app.on('ready', setup);
|
||||
}
|
||||
|
||||
// Method to update notification config
|
||||
function updateConfig(customConfig) {
|
||||
// Fetching user preferred notification position from config
|
||||
if (customConfig.position) {
|
||||
config = Object.assign(config, {startCorner: customConfig.position});
|
||||
|
||||
calcDimensions();
|
||||
}
|
||||
|
||||
// Fetching user preferred notification screen from config
|
||||
if (customConfig.display) {
|
||||
displayId = customConfig.display;
|
||||
}
|
||||
}
|
||||
|
||||
function setup() {
|
||||
setupConfig();
|
||||
|
||||
@@ -208,12 +227,21 @@ function calcDimensions() {
|
||||
function setupConfig() {
|
||||
closeAll();
|
||||
|
||||
// Use primary display only
|
||||
let display = electron.screen.getPrimaryDisplay();
|
||||
// This feature only applies to windows
|
||||
if (!isMac) {
|
||||
let screens = electron.screen.getAllDisplays();
|
||||
if (screens && screens.length >= 0) {
|
||||
externalDisplay = screens.find((screen) => {
|
||||
let screenId = screen.id.toString();
|
||||
return screenId === displayId;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let display = externalDisplay ? externalDisplay : electron.screen.getPrimaryDisplay();
|
||||
config.corner = {};
|
||||
config.corner.x = display.bounds.x + display.workArea.x;
|
||||
config.corner.y = display.bounds.y + display.workArea.y;
|
||||
config.corner.x = display.workArea.x;
|
||||
config.corner.y = display.workArea.y;
|
||||
|
||||
// update corner x/y based on corner of screen where notf should appear
|
||||
const workAreaWidth = display.workAreaSize.width;
|
||||
@@ -242,7 +270,6 @@ function setupConfig() {
|
||||
config.maxVisibleNotifications = config.maxVisibleNotifications > 5 ? 5 : config.maxVisibleNotifications;
|
||||
}
|
||||
|
||||
|
||||
function notify(notification) {
|
||||
// Is it an object and only one argument?
|
||||
if (arguments.length === 1 && typeof notification === 'object') {
|
||||
@@ -650,4 +677,5 @@ function cleanUpInactiveWindow() {
|
||||
}
|
||||
|
||||
module.exports.notify = notify
|
||||
module.exports.updateConfig = updateConfig
|
||||
module.exports.reset = setupConfig
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
'use strict';
|
||||
|
||||
const electron = require('electron');
|
||||
const ipc = electron.ipcRenderer;
|
||||
|
||||
let availableScreens;
|
||||
let selectedPosition;
|
||||
let selectedDisplay;
|
||||
|
||||
renderSettings();
|
||||
|
||||
// Method that renders the data from user config
|
||||
function renderSettings() {
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
let okButton = document.getElementById('ok-button');
|
||||
let cancel = document.getElementById('cancel');
|
||||
|
||||
okButton.addEventListener('click', function () {
|
||||
selectedPosition = document.querySelector('input[name="position"]:checked').value;
|
||||
let selector = document.getElementById('screen-selector');
|
||||
selectedDisplay = selector.options[selector.selectedIndex].value;
|
||||
|
||||
// update the user selected data and close the window
|
||||
updateAndClose();
|
||||
});
|
||||
|
||||
cancel.addEventListener('click', function () {
|
||||
ipc.send('close-alert');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function updateAndClose() {
|
||||
ipc.send('update-config', {position: selectedPosition, display: selectedDisplay});
|
||||
ipc.send('close-alert');
|
||||
}
|
||||
|
||||
ipc.on('notificationSettings', (event, args) => {
|
||||
// update position from user config
|
||||
if (args && args.position) {
|
||||
document.getElementById(args.position).checked = true;
|
||||
}
|
||||
|
||||
// update selected display from user config
|
||||
if (args && args.display) {
|
||||
if (availableScreens) {
|
||||
let index = availableScreens.findIndex((item) => {
|
||||
let id = item.id.toString();
|
||||
return id === args.display;
|
||||
});
|
||||
if (index !== -1){
|
||||
let option = document.getElementById(availableScreens[index].id);
|
||||
|
||||
if (option){
|
||||
option.selected = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ipc.on('screens', (event, screens) => {
|
||||
availableScreens = screens;
|
||||
let screenSelector = document.getElementById('screen-selector');
|
||||
|
||||
if (screenSelector && screens){
|
||||
|
||||
// clearing the previously added content to
|
||||
// make sure the content is not repeated
|
||||
screenSelector.innerHTML = '';
|
||||
|
||||
screens.forEach((scr, index) => {
|
||||
let option = document.createElement('option');
|
||||
option.value = scr.id;
|
||||
option.id = scr.id;
|
||||
option.innerHTML = index + 1;
|
||||
screenSelector.appendChild(option);
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,91 @@
|
||||
html, body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
.content {
|
||||
border-radius: 2px;
|
||||
width: 100%;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
line-height: 1.3;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid rgba(0, 0, 0, .1);
|
||||
margin-bottom: 20px;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.header__title {
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
margin: 0 0 0 4px;
|
||||
font-size: 1.4rem;
|
||||
min-height: 13px;
|
||||
text-overflow: ellipsis;
|
||||
text-align: center;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
color: rgba(0, 0, 0, .8);
|
||||
}
|
||||
|
||||
.form {
|
||||
width: 95%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.selector {
|
||||
padding: 0 9px 0 16px;
|
||||
cursor: pointer;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 20px;
|
||||
height: 100%;
|
||||
border: 1px solid #ccc !important;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.main .first-set {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.main .second-set {
|
||||
flex-grow: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.radio {
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.radio__label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.radio__label input {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.footer {
|
||||
padding: 12px 10px;
|
||||
border-top: 1px solid rgba(0, 0, 0, .1);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.buttonLayout {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.buttonDismiss {
|
||||
margin-right: 10px;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Symphony - Configure Notification Position</title>
|
||||
<link rel="stylesheet" type="text/css" href="./configure-notification-position.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="content">
|
||||
<header class="header">
|
||||
<span class="header__title">Notification Settings</span>
|
||||
</header>
|
||||
<div class="form">
|
||||
<form>
|
||||
<label class="label">Monitor</label>
|
||||
<div id="screens" class="main">
|
||||
<label>Notification shown on Monitor: </label>
|
||||
<select class="selector" id="screen-selector">
|
||||
</select>
|
||||
</div>
|
||||
<label class="label">Position</label>
|
||||
<div class="main">
|
||||
<div class="first-set">
|
||||
<div class="radio">
|
||||
<label class="radio__label"><input id="upper-left" type="radio" name="position" value="upper-left">
|
||||
Top Left
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label class="radio__label">
|
||||
<input id="lower-left" type="radio" name="position" value="lower-left">
|
||||
Bottom Left
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="second-set">
|
||||
<div class="radio">
|
||||
<label class="radio__label">Top Right
|
||||
<input id="upper-right" type="radio" name="position" value="upper-right">
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label class="radio__label">Bottom Right
|
||||
<input id="lower-right" type="radio" name="position" value="lower-right">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="buttonLayout">
|
||||
<button id="cancel" class="buttonDismiss">CANCEL</button>
|
||||
<button id="ok-button" class="button">OK</button>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,152 @@
|
||||
'use strict';
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const electron = require('electron');
|
||||
const BrowserWindow = electron.BrowserWindow;
|
||||
const app = electron.app;
|
||||
const ipc = electron.ipcMain;
|
||||
const log = require('../../log.js');
|
||||
const logLevels = require('../../enums/logLevels.js');
|
||||
const notify = require('./../electron-notify');
|
||||
const eventEmitter = require('./../../eventEmitter');
|
||||
|
||||
const { updateConfigField } = require('../../config');
|
||||
|
||||
let configurationWindow;
|
||||
let screens;
|
||||
let position;
|
||||
let display;
|
||||
|
||||
let windowConfig = {
|
||||
width: 460,
|
||||
height: 360,
|
||||
show: false,
|
||||
modal: true,
|
||||
autoHideMenuBar: true,
|
||||
resizable: false,
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, 'configure-notification-position-preload.js'),
|
||||
sandbox: true,
|
||||
nodeIntegration: false
|
||||
}
|
||||
};
|
||||
|
||||
app.on('ready', () => {
|
||||
screens = electron.screen.getAllDisplays();
|
||||
|
||||
// if display added/removed update the list of screens
|
||||
electron.screen.on('display-added', updateScreens);
|
||||
electron.screen.on('display-removed', updateScreens);
|
||||
});
|
||||
|
||||
function updateScreens() {
|
||||
screens = electron.screen.getAllDisplays();
|
||||
|
||||
// Notifying renderer when a display is added/removed
|
||||
if (configurationWindow && screens && screens.length >= 0) {
|
||||
configurationWindow.webContents.send('screens', screens);
|
||||
}
|
||||
}
|
||||
|
||||
function getTemplatePath() {
|
||||
let templatePath = path.join(__dirname, 'configure-notification-position.html');
|
||||
try {
|
||||
fs.statSync(templatePath).isFile();
|
||||
} catch (err) {
|
||||
log.send(logLevels.ERROR, 'configure-notification-position: Could not find template ("' + templatePath + '").');
|
||||
}
|
||||
return 'file://' + templatePath;
|
||||
}
|
||||
|
||||
function openConfigurationWindow(windowName) {
|
||||
let allWindows = BrowserWindow.getAllWindows();
|
||||
allWindows = allWindows.find((window) => { return window.winName === windowName });
|
||||
|
||||
// if we couldn't find any window matching the window name
|
||||
// it will render as a new window
|
||||
if (allWindows) {
|
||||
windowConfig.parent = allWindows;
|
||||
}
|
||||
|
||||
configurationWindow = new BrowserWindow(windowConfig);
|
||||
configurationWindow.setVisibleOnAllWorkspaces(true);
|
||||
configurationWindow.loadURL(getTemplatePath());
|
||||
|
||||
configurationWindow.once('ready-to-show', () => {
|
||||
configurationWindow.show();
|
||||
});
|
||||
|
||||
configurationWindow.webContents.on('did-finish-load', () => {
|
||||
if (screens && screens.length >= 0) {
|
||||
configurationWindow.webContents.send('screens', screens);
|
||||
}
|
||||
configurationWindow.webContents.send('notificationSettings', {position: position, display: display});
|
||||
});
|
||||
|
||||
configurationWindow.on('close', () => {
|
||||
destroyWindow();
|
||||
});
|
||||
|
||||
configurationWindow.on('closed', () => {
|
||||
destroyWindow();
|
||||
});
|
||||
}
|
||||
|
||||
function destroyWindow() {
|
||||
configurationWindow = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to save 'position' & 'display' to the config file
|
||||
*/
|
||||
function updateConfig() {
|
||||
let settings = {
|
||||
position: position,
|
||||
display: display
|
||||
};
|
||||
updateConfigField('notificationSettings', settings);
|
||||
updateNotification(position, display);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to update the Notification class with the new 'position' & 'screen'
|
||||
* @param mPosition - position to display the notifications
|
||||
* ('upper-right, upper-left, lower-right, lower-left')
|
||||
* @param mDisplay - id of the selected display
|
||||
*/
|
||||
function updateNotification(mPosition, mDisplay) {
|
||||
notify.updateConfig({position: mPosition, display: mDisplay});
|
||||
notify.reset();
|
||||
}
|
||||
|
||||
ipc.on('close-alert', function () {
|
||||
configurationWindow.close();
|
||||
});
|
||||
|
||||
ipc.on('update-config', (event, config) => {
|
||||
|
||||
if (config) {
|
||||
if (config.position) {
|
||||
position = config.position;
|
||||
}
|
||||
if (config.display) {
|
||||
display = config.display;
|
||||
}
|
||||
}
|
||||
|
||||
updateConfig();
|
||||
});
|
||||
|
||||
/**
|
||||
* Event to read 'Position' & 'Display' from config and
|
||||
* updated the configuration view
|
||||
*/
|
||||
eventEmitter.on('notificationSettings', (notificationSettings) => {
|
||||
position = notificationSettings.position;
|
||||
display = notificationSettings.display;
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
openConfigurationWindow: openConfigurationWindow
|
||||
};
|
||||
@@ -209,7 +209,18 @@ function createAPI() {
|
||||
* see: https://electron.atom.io/docs/api/desktop-capturer/
|
||||
* for interface: see documentation in desktopCapturer/getSources.js
|
||||
*/
|
||||
getMediaSources: getMediaSources
|
||||
getMediaSources: getMediaSources,
|
||||
|
||||
/**
|
||||
* Opens a modal window to configure notification preference.
|
||||
*/
|
||||
showNotificationSettings: function() {
|
||||
let windowName = remote.getCurrentWindow().winName;
|
||||
local.ipcRenderer.send(apiName, {
|
||||
cmd: apiCmds.showNotificationSettings,
|
||||
windowName: windowName
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// add support for both ssf and SYM_API name-space.
|
||||
|
||||
@@ -33,6 +33,8 @@ let willQuitApp = false;
|
||||
let isOnline = true;
|
||||
let boundsChangeWindow;
|
||||
let alwaysOnTop = false;
|
||||
let position = 'lower-right';
|
||||
let display;
|
||||
|
||||
// note: this file is built using browserify in prebuild step.
|
||||
const preloadMainScript = path.join(__dirname, 'preload/_preloadMain.js');
|
||||
@@ -140,6 +142,8 @@ function doCreateMainWindow(initialUrl, initialBounds) {
|
||||
if (!isOnline) {
|
||||
loadErrors.showNetworkConnectivityError(mainWindow, url, retry);
|
||||
} else {
|
||||
// updates the notify config with user preference
|
||||
notify.updateConfig({position: position, display: display});
|
||||
// removes all existing notifications when main window reloads
|
||||
notify.reset();
|
||||
log.send(logLevels.INFO, 'loaded main window url: ' + url);
|
||||
@@ -416,6 +420,12 @@ eventEmitter.on('isAlwaysOnTop', (boolean) => {
|
||||
isAlwaysOnTop(boolean);
|
||||
});
|
||||
|
||||
// node event emitter for notification settings
|
||||
eventEmitter.on('notificationSettings', (notificationSettings) => {
|
||||
position = notificationSettings.position;
|
||||
display = notificationSettings.display;
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
createMainWindow: createMainWindow,
|
||||
getMainWindow: getMainWindow,
|
||||
|
||||
Reference in New Issue
Block a user