Merge pull request #134 from Lplenka/master

Added unread counts in tray icon Fixes #110
This commit is contained in:
Akash Nimare
2017-04-18 00:26:36 +05:30
committed by GitHub
6 changed files with 241 additions and 24 deletions

View File

@@ -8,7 +8,7 @@ function appUpdater() {
log.transports.file.level = 'info';
autoUpdater.logger = log;
/*
autoUpdater.on('error', err => log.info(err));
AutoUpdater.on('error', err => log.info(err));
autoUpdater.on('checking-for-update', () => log.info('checking-for-update'));
autoUpdater.on('update-available', () => log.info('update-available'));
autoUpdater.on('update-not-available', () => log.info('update-not-available'));
@@ -17,7 +17,7 @@ function appUpdater() {
// Ask the user if update is available
// eslint-disable-next-line no-unused-vars
autoUpdater.on('update-downloaded', (event, info) => {
// let message = app.getName() + ' ' + info.releaseName + ' is now available. It will be installed the next time you restart the application.';
// Let message = app.getName() + ' ' + info.releaseName + ' is now available. It will be installed the next time you restart the application.';
// if (info.releaseNotes) {
// const splitNotes = info.releaseNotes.split(/[^\r]\n/);
// message += '\n\nRelease notes:\n';
@@ -38,7 +38,7 @@ function appUpdater() {
}
});
});
// init for updates
// Init for updates
autoUpdater.checkForUpdates();
}

View File

@@ -12,7 +12,7 @@ const electronLocalshortcut = require('electron-localshortcut');
const Configstore = require('electron-config');
const JsonDB = require('node-json-db');
const isDev = require('electron-is-dev');
const tray = require('./tray');
// Not using now //const tray = require('./tray');
const appMenu = require('./menu');
const {linkIsInternal, skipImages} = require('./link-helper');
const {appUpdater} = require('./autoupdater');
@@ -20,7 +20,7 @@ const {appUpdater} = require('./autoupdater');
const db = new JsonDB(app.getPath('userData') + '/domain.json', true, true);
const data = db.getData('/');
// adds debug features like hotkeys for triggering dev tools and reload
// Adds debug features like hotkeys for triggering dev tools and reload
require('electron-debug')();
const conf = new Configstore();
@@ -41,10 +41,10 @@ function userOS() {
}
}
// setting userAgent so that server-side code can identify the desktop app
// Setting userAgent so that server-side code can identify the desktop app
const isUserAgent = 'ZulipElectron/' + app.getVersion() + ' ' + userOS();
// prevent window being garbage collected
// Prevent window being garbage collected
let mainWindow;
let targetLink;
@@ -93,6 +93,7 @@ function checkConnectivity() {
}, index => {
if (index === 0) {
mainWindow.webContents.reload();
mainWindow.webContents.send('destroytray');
}
if (index === 1) {
app.quit();
@@ -158,6 +159,7 @@ function updateDockBadge(title) {
if (process.platform === 'darwin') {
app.setBadgeCount(messageCount);
}
mainWindow.webContents.send('tray', messageCount);
}
function createMainWindow() {
@@ -217,7 +219,7 @@ function createMainWindow() {
});
});
// on osx it's 'moved'
// On osx it's 'moved'
win.on('move', function () {
const pos = this.getPosition();
conf.set({
@@ -226,12 +228,19 @@ function createMainWindow() {
});
});
// stop page to update it's title
// Stop page to update it's title
win.on('page-title-updated', (e, title) => {
e.preventDefault();
updateDockBadge(title);
});
// To destroy tray icon when navigate to a new URL
win.webContents.on('will-navigate', e => {
if (e) {
win.webContents.send('destroytray');
}
});
return win;
}
@@ -239,7 +248,7 @@ function createMainWindow() {
app.commandLine.appendSwitch('ignore-certificate-errors', 'true');
app.on('window-all-closed', () => {
// unregister all the shortcuts so that they don't interfare with other apps
// Unregister all the shortcuts so that they don't interfare with other apps
electronLocalshortcut.unregisterAll(mainWindow);
if (process.platform !== 'darwin') {
app.quit();
@@ -255,13 +264,14 @@ app.on('activate', () => {
app.on('ready', () => {
electron.Menu.setApplicationMenu(appMenu);
mainWindow = createMainWindow();
tray.create();
// Not using for now // tray.create();
const page = mainWindow.webContents;
// TODO - use global shortcut instead
electronLocalshortcut.register(mainWindow, 'CommandOrControl+R', () => {
mainWindow.reload();
mainWindow.webContents.send('destroytray');
});
electronLocalshortcut.register(mainWindow, 'CommandOrControl+[', () => {
@@ -301,19 +311,22 @@ app.on('ready', () => {
});
app.on('will-quit', () => {
// unregister all the shortcuts so that they don't interfare with other apps
// Unregister all the shortcuts so that they don't interfare with other apps
electronLocalshortcut.unregisterAll(mainWindow);
});
ipc.on('new-domain', (e, domain) => {
// mainWindow.loadURL(domain);
// MainWindow.loadURL(domain);
if (!mainWindow) {
mainWindow = createMainWindow();
mainWindow.loadURL(domain);
mainWindow.webContents.send('destroytray');
} else if (mainWindow.isMinimized()) {
mainWindow.webContents.send('destroytray');
mainWindow.loadURL(domain);
mainWindow.show();
} else {
mainWindow.webContents.send('destroytray');
mainWindow.loadURL(domain);
serverError(domain);
}

View File

@@ -8,7 +8,7 @@ const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const shell = electron.shell;
const appName = app.getName();
const tray = require('./tray');
// Const tray = require('./tray');
const {addDomain, about} = require('./windowmanager');
@@ -36,6 +36,7 @@ const viewSubmenu = [
click(item, focusedWindow) {
if (focusedWindow) {
focusedWindow.reload();
focusedWindow.webContents.send('destroytray');
}
}
},
@@ -79,7 +80,7 @@ const viewSubmenu = [
label: 'Toggle Tray Icon',
click(item, focusedWindow) {
if (focusedWindow) {
tray.toggle();
focusedWindow.webContents.send('toggletray');
}
}
},

View File

@@ -12,8 +12,11 @@ process.once('loaded', () => {
// eslint-disable-next-line import/no-unassigned-import
require('./domain');
// eslint-disable-next-line import/no-unassigned-import
require('../renderer/js/tray.js');
// Calling Tray.js in renderer process everytime app window loads
// handle zooming functionality
// Handle zooming functionality
const zoomIn = () => {
webFrame.setZoomFactor(webFrame.getZoomFactor() + 0.1);
};
@@ -26,7 +29,7 @@ const zoomActualSize = () => {
webFrame.setZoomFactor(1);
};
// get zooming actions from main process
// Get zooming actions from main process
ipcRenderer.on('zoomIn', () => {
zoomIn();
});
@@ -40,7 +43,7 @@ ipcRenderer.on('zoomActualSize', () => {
});
ipcRenderer.on('log-out', () => {
// create the menu for the below
// Create the menu for the below
document.querySelector('.dropdown-toggle').click();
const nodes = document.querySelectorAll('.dropdown-menu li:last-child a');
@@ -48,19 +51,19 @@ ipcRenderer.on('log-out', () => {
});
ipcRenderer.on('shortcut', () => {
// create the menu for the below
// Create the menu for the below
const node = document.querySelector('a[data-overlay-trigger=keyboard-shortcuts]');
// additional check
// Additional check
if (node.text.trim().toLowerCase() === 'keyboard shortcuts') {
node.click();
} else {
// atleast click the dropdown
// Atleast click the dropdown
document.querySelector('.dropdown-toggle').click();
}
});
// To prevent failing this script on linux we need to load it after the document loaded
document.addEventListener('DOMContentLoaded', () => {
// init spellchecker
// Init spellchecker
spellChecker();
});

View File

@@ -1,6 +1,7 @@
'use strict';
const path = require('path');
const electron = require('electron');
const ipc = require('electron').ipcMain;
const APP_ICON = path.join(__dirname, '../resources', 'Icon');
@@ -11,7 +12,7 @@ let domainWindow;
let aboutWindow;
function onClosed() {
// dereference the window
// Dereference the window
domainWindow = null;
aboutWindow = null;
}
@@ -64,7 +65,7 @@ function createAboutWindow() {
aboutwin.loadURL(aboutURL);
aboutwin.on('closed', onClosed);
// stop page to update it's title
// Stop page to update it's title
aboutwin.on('page-title-updated', e => {
e.preventDefault();
});
@@ -82,6 +83,17 @@ function about() {
});
}
ipc.on('trayabout', event => {
if (event) {
about();
}
});
ipc.on('traychangeserver', event => {
if (event) {
addDomain();
}
});
module.exports = {
addDomain,
about

188
app/renderer/js/tray.js Normal file
View File

@@ -0,0 +1,188 @@
'use strict';
const path = require('path');
const electron = require('electron');
const {ipcRenderer, remote} = electron;
const {Tray, Menu, nativeImage} = remote;
const APP_ICON = path.join(__dirname, '../../resources/tray', 'tray');
const iconPath = () => {
if (process.platform === 'linux') {
return APP_ICON + 'linux.png';
}
return APP_ICON + (process.platform === 'win32' ? 'win.ico' : 'osx.png');
};
let unread = 0;
const trayIconSize = () => {
switch (process.platform) {
case 'darwin':
return 20;
case 'win32':
return 100;
case 'linux':
return 100;
default: return 80;
}
};
// Default config for Icon we might make it OS specific if needed like the size
const config = {
pixelRatio: window.devicePixelRatio,
unreadCount: 0,
showUnreadCount: true,
unreadColor: '#000000',
readColor: '#000000',
unreadBackgroundColor: '#B9FEEA',
readBackgroundColor: '#B9FEEA',
size: trayIconSize(),
thick: process.platform === 'win32'
};
const renderCanvas = function (arg) {
config.unreadCount = arg;
return new Promise((resolve, reject) => {
const SIZE = config.size * config.pixelRatio;
const PADDING = SIZE * 0.05;
const CENTER = SIZE / 2;
const HAS_COUNT = config.showUnreadCount && config.unreadCount;
const color = config.unreadCount ? config.unreadColor : config.readColor;
const backgroundColor = config.unreadCount ? config.unreadBackgroundColor : config.readBackgroundColor;
const canvas = document.createElement('canvas');
canvas.width = SIZE;
canvas.height = SIZE;
const ctx = canvas.getContext('2d');
// Circle
// If (!config.thick || config.thick && HAS_COUNT) {
ctx.beginPath();
ctx.arc(CENTER, CENTER, (SIZE / 2) - PADDING, 0, 2 * Math.PI, false);
ctx.fillStyle = backgroundColor;
ctx.fill();
ctx.lineWidth = SIZE / (config.thick ? 10 : 20);
ctx.strokeStyle = backgroundColor;
ctx.stroke();
// Count or Icon
if (HAS_COUNT) {
ctx.fillStyle = color;
ctx.textAlign = 'center';
if (config.unreadCount > 99) {
ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.4}px Helvetica`;
ctx.fillText('99+', CENTER, CENTER + (SIZE * 0.15));
} else if (config.unreadCount < 10) {
ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.5}px Helvetica`;
ctx.fillText(config.unreadCount, CENTER, CENTER + (SIZE * 0.20));
} else {
ctx.font = `${config.thick ? 'bold ' : ''}${SIZE * 0.5}px Helvetica`;
ctx.fillText(config.unreadCount, CENTER, CENTER + (SIZE * 0.15));
}
resolve(canvas);
} else {
reject(canvas);
}
});
};
/**
* Renders the tray icon as a native image
* @param arg: Unread count
* @return the native image
*/
const renderNativeImage = function (arg) {
return Promise.resolve()
.then(() => renderCanvas(arg))
.then(canvas => {
const pngData = nativeImage.createFromDataURL(canvas.toDataURL('image/png')).toPng();
return Promise.resolve(nativeImage.createFromBuffer(pngData, config.pixelRatio));
});
};
const createTray = function () {
window.tray = new Tray(iconPath());
const contextMenu = Menu.buildFromTemplate([{
label: 'About',
click() {
ipcRenderer.send('trayabout');
}
},
{
type: 'separator'
},
{
label: 'Change Zulip server',
click() {
ipcRenderer.send('traychangeserver');
}
},
{
type: 'separator'
},
{
label: 'Reload',
click() {
remote.getCurrentWindow().reload();
window.tray.destroy();
}
},
{
type: 'separator'
},
{
label: 'Quit',
click() {
remote.getCurrentWindow().close();
}
}
]);
window.tray.setContextMenu(contextMenu);
};
ipcRenderer.on('destroytray', event => {
window.tray.destroy();
if (window.tray.isDestroyed()) {
window.tray = null;
} else {
throw new Error('Tray icon not properly destroyed.');
}
return event;
});
ipcRenderer.on('tray', (event, arg) => {
if (arg === 0) {
unread = arg;
// Message Count // console.log("message count is zero.");
window.tray.setImage(iconPath());
window.tray.setToolTip('No unread messages');
} else {
unread = arg;
renderNativeImage(arg).then(image => {
window.tray.setImage(image);
window.tray.setToolTip(arg + ' unread messages');
});
}
});
ipcRenderer.on('toggletray', event => {
if (event) {
if (window.tray) {
window.tray.destroy();
if (window.tray.isDestroyed()) {
window.tray = null;
}
} else {
createTray();
renderNativeImage(unread).then(image => {
window.tray.setImage(image);
window.tray.setToolTip(unread + ' unread messages');
});
}
}
});
createTray();