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:
Kiran Niranjan
2017-06-16 15:29:56 -07:00
committed by Lynn
parent d5f04ce152
commit 2b6ec2aeb8
12 changed files with 469 additions and 7 deletions
+6 -1
View File
@@ -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": ""
}
}
+7
View File
@@ -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;
+1
View File
@@ -11,6 +11,7 @@ const cmds = keyMirror({
registerBoundsChange: null,
registerProtocolHandler: null,
registerActivityDetection: null,
showNotificationSettings: null,
});
module.exports = {
+5
View File
@@ -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...
+9
View File
@@ -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(){
+33 -5
View File
@@ -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
};
+12 -1
View File
@@ -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.
+10
View File
@@ -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,