Skip to content

Commit

Permalink
Merge pull request RocketChat#315 from alexbrazier/feature/notifications
Browse files Browse the repository at this point in the history
Fix issues with Windows 7 notifications and improve design
  • Loading branch information
rodrigok authored Jan 24, 2017
2 parents 7639eed + dc94d48 commit d21ccdd
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 107 deletions.
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,6 @@
"dependencies": {
"fs-jetpack": "^0.10.5",
"@paulcbetts/system-idle-time": "^1.0.4",
"electron-notification-shim": "^1.1.0",
"electron-toaster": "^2.0.2",
"spellchecker": "^3.3.1",
"lodash": "^4.17.4",
"electron-rebuild": "^1.5.7"
Expand Down
66 changes: 66 additions & 0 deletions src/Toaster.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { BrowserWindow, ipcMain, screen } from 'electron';

export default class Toaster {
constructor (mainWindow, maxNotifications = 3, debug = false) {
this.mainWindow = mainWindow;
this.debug = debug;
this.maxNotifications = maxNotifications;
this.windows = [];
}

toast (msg, callback) {
const window = new BrowserWindow({
width: msg.width,
height: 75,
useContentSize: true,
transparent: true,
frame: false,
show : false,
alwaysOnTop: true,
skipTaskbar: true,
resizeable: false
});

if (this.debug) {
window.openDevTools();
}

this.windows.push(window);

ipcMain.once(`notification-${msg.tag}`, callback);

window.on('closed', () => {
this.windows = this.windows.filter((win) => win && !win.isDestroyed() && win !== window);
this.windows.forEach((win, i) => this._setPosition(win, i + 1));
});

const htmlFile = `${msg.htmlFile}?` +
`title=${encodeURIComponent(msg.title || '')}&` +
`message=${encodeURIComponent(msg.message || '')}&` +
`timeout=${msg.timeout}&` +
`icon=${msg.icon}&` +
`tag=${msg.tag}`;

window.loadURL(htmlFile);

window.webContents.on('did-finish-load', () => {
this._setPosition(window, this.windows.length);
});
}

_setPosition (window, index) {
const width = window.getSize()[0];
const height = window.getSize()[1];
const pos = this.mainWindow.getPosition();
const display = screen.getDisplayNearestPoint({x:pos[0], y:pos[1]});
const notificationDistance = height + 5;
const x = display.workAreaSize.width - width - 4;
const y = display.workAreaSize.height - (notificationDistance * index);

window.setPosition(x, y);

if (index <= this.maxNotifications) {
window.show();
}
}
}
40 changes: 17 additions & 23 deletions src/background.custom.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import os from 'os';
import { app, ipcMain } from 'electron';
import windowStateKeeper from './background/windowState';
import certificate from './background/certificate';
import Toaster from 'electron-toaster';
import Toaster from './Toaster';
import idle from '@paulcbetts/system-idle-time';

process.env.GOOGLE_API_KEY = 'AIzaSyADqUh_c1Qhji3Cp1NE43YrcpuPkmhXD-c';
Expand Down Expand Up @@ -90,31 +90,25 @@ export function afterMainWindow (mainWindow) {
event.preventDefault();
});

// ==== Quick check to fetch Operating System and it's version ==>>
// Add here any OS without native support for notifications to Toaster is used
var useToaster = false;
ipcMain.on('focus', () => mainWindow.show());

// Windows 7 or older
if (os.platform() === 'win32' || os.platform() === 'win64') {
if (parseFloat(os.release()) < 6.2) {
useToaster = true;
}
}
// Windows 7 and below
const useToaster = ['win32', 'win64'].indexOf(os.platform()) !== -1 &&
parseFloat(os.release()) < 6.2;

if (useToaster) {
const toaster = new Toaster();
toaster.init(mainWindow);

ipcMain.on('notification-shim', (e, msg) => {
mainWindow.webContents.executeJavaScript(`
require('electron').ipcRenderer.send('electron-toaster-message', {
title: '${msg.title}',
message: \`${msg.options.body}\`,
width: 400,
focus: false,
htmlFile: 'file://'+__dirname+'/notification.html?'
});
`);
const toaster = new Toaster(mainWindow);

ipcMain.on('notification-shim', (e, title, options) => {
toaster.toast({
title: title,
message: options.body,
icon: options.icon,
tag: options.tag,
width: 400,
timeout: 5000,
htmlFile: 'file://'+__dirname+'/public/notification.html'
}, () => e.sender.send(`clicked-${options.tag}`));
});
}

Expand Down
220 changes: 145 additions & 75 deletions src/public/notification.html
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
<!DOCTYPE html>
<html>
<head>


<title>toaster</title>
<!-- <link rel="stylesheet" type="text/css" href="bower_components/material-design-lite/material.min.css"> -->
<link rel="stylesheet" type="text/css" href="bower_components/octicons/octicons/octicons.css">
<title>Toaster</title>
<style type="text/css">
*{margin: 0;padding:0;}
body, html{
Expand All @@ -15,105 +11,179 @@
overflow: hidden;
}

.ico{
color: white;
font-size: 32px;
padding: 11px;
#frame {
height: 75px;
width: 100%;
background-color: #044b76;
position: absolute;
border-width: 2px;
border-style: solid;
border-color: #04436a;
overflow: hidden;
bottom: -4px;
}

#title{
font-weight:bold;
font-size: 18px;
#frame.enter {
-webkit-animation-duration: 0.5s;
-webkit-animation-fill-mode: both;
-webkit-animation-name: enter;
}

#message{
font-size: 14px;
#frame.leave {
-webkit-animation-duration: 0.5s;
-webkit-animation-fill-mode: both;
-webkit-animation-name: leave;
align-self: flex-end;
}

#detail{
font-weight:bold;
font-size: 12px;
color: red;
@-webkit-keyframes enter {
0% {
-webkit-transform: perspective(300px) rotateY(90deg);
}
100% {
-webkit-transform: none;
}
}
@-webkit-keyframes leave {
0% {
opacity: 1;
}
100% {
opacity: 0;
height: 0;
margin-top: 0;
margin-bottom: 0;
}
}
.content {
position: relative;
height: 75px;
}
.icon {
width: 75px;
background-color: #04436a;
float: left;
}

table{
width: 100%;
.icon img {
margin: 10px;
width: 50px;
}
.text {
font-size: 0.8em;
padding-left: 1em;
line-height: 16px;
float: left;
color: white;
}

table td {
vertical-align: top;
.title {
font-weight: bold;
margin-top: 0.5em;
margin-bottom: 0;
padding-right: 2em;
-webkit-line-clamp: 1;
overflow: hidden;
text-overflow: ellipsis;
}
.message {
margin-top: 0.33em;
padding-right: 4em;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
}

.close {
position: absolute;
right: 9px;
text-decoration: none;
cursor: pointer;
font-weight: bold;
opacity: 0.2;
-webkit-transition: all 0.4s ease-out;
color: white;
}
.content:hover > .close {
opacity: 1;
-webkit-transition: all 0.25s ease-in;
}
.logo {
width: 18px;
height: 18px;
position: absolute;
bottom: 6px;
right: 6px;
}
</style>
</head>
<body>
<table id="content">
<tbody>
<row>
<td style="background-color:silver;" width="90" cellpadding="0">
<div style="text-align:center;">
<br>
<img src="images/icon.png" style="width: 50px" />
</div>
</td>
<td cellpadding="30">
<div style="padding:22px;">
<span id="title"></span><hr>
<span id="message"></span><br><br><br>
<span id="detail"></span>
</div>
</td>
</row>
</tbody>
</table>
<div id="frame" class="enter">
<div class="content">
<div class="icon">
<img id="icon" />
</div>
<div class="text">
<p class="title" id="title"></p>
<p class="message" id="message"></p>
</div>
<img class="logo" src="images/icon.png"/>
<a id="close" class="close">
&times;
</a>
</div>
</div>
<script>

var timeout;
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
var vars = query.split("&");
var vars = query.split('&');
for (var i=0;i<vars.length;i++) {
var pair = vars[i].split("=");
var pair = vars[i].split('=');
if (pair[0] === variable) {
return decodeURIComponent(pair[1]);
}
}
}

var autoSize = function() {
var heightOffset = window.outerHeight - window.innerHeight;
var widthOffset = window.outerWidth - window.innerWidth;
var result = {
height : document.getElementById("content").clientHeight + heightOffset,
width : document.getElementById("content").clientWidth + widthOffset
}

window.resizeTo(result.width, result.height);
function clicked() {
require('electron').ipcRenderer.send(`notification-${getQueryVariable('tag')}`);
closeItem();
}

return result;
};
function mouseOver() {
clearTimeout(timeout);
}

function startTimeout() {
timeout = setTimeout(function() {
closeItem();
}, parseInt(getQueryVariable('timeout')));
}

var onKeydown = function(/*e*/) {
window.close();
};
function closeItem() {
document.getElementById('frame').className = 'leave';
setTimeout(function() {
window.close();
}, 500);
}

var onLoad = function load(/*event*/){
autoSize();
this.removeEventListener("load", load, false); //remove listener, no longer needed
function closeClick(e) {
e.stopPropagation();
closeItem();
}

this.setTimeout(function() {
this.close();
}, parseInt(getQueryVariable("timeout")));
window.onload = function() {
startTimeout();
var frame = document.getElementById('frame');
frame.addEventListener('click', clicked)
frame.addEventListener('mouseover', mouseOver)
frame.addEventListener('mouseout', startTimeout)

document.addEventListener("keydown", onKeydown, false);
document.addEventListener("click", window.close);
};
document.getElementById('close').addEventListener('click', closeClick);
}

document.getElementById("title").innerHTML = getQueryVariable("title");
document.getElementById("message").innerHTML = getQueryVariable("message");
document.getElementById("detail").innerHTML = getQueryVariable("detail");
window.addEventListener("load", onLoad, false);
document.getElementById('title').innerHTML = getQueryVariable('title');
document.getElementById('message').innerHTML = getQueryVariable('message');
document.getElementById('icon').setAttribute('src', getQueryVariable('icon'));
</script>
</body>
</html>


Loading

0 comments on commit d21ccdd

Please sign in to comment.