Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FIX] System tray, dock, task bar and main window issues #959

Merged
merged 25 commits into from
Oct 30, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
008101b
Avoid tray icon recreation
tassoevan Oct 27, 2018
a99b398
Move the responsability for tray icon images to renderer process
tassoevan Oct 22, 2018
5b2610a
Render tray icon in renderer process
tassoevan Oct 27, 2018
d7f3d1b
Render template tray icon for Mac OS
tassoevan Oct 28, 2018
bc73af4
Fix platform-specific tray icon status rendering
tassoevan Oct 28, 2018
0cec2b2
Add icon module
tassoevan Oct 28, 2018
ffce670
Remove obsolete tray icon images
tassoevan Oct 28, 2018
7bf878a
Add console logging for promise rejections
tassoevan Oct 28, 2018
3bfba1a
Apply icon to the main window
tassoevan Oct 28, 2018
dd702c9
Refactor code
tassoevan Oct 28, 2018
c8e5383
Remove drop shadow from icon
tassoevan Oct 28, 2018
abbf6b8
Memoize icon rendering
tassoevan Oct 28, 2018
e44c120
Fix some bugs
tassoevan Oct 28, 2018
7eec1ca
Remove unused module
tassoevan Oct 29, 2018
522a94a
Add multisize icons
tassoevan Oct 29, 2018
21e1d87
Fix bug in tray module
tassoevan Oct 29, 2018
8545766
Change communication between events and main window
tassoevan Oct 29, 2018
1ca61e8
Update SVG icon
tassoevan Oct 29, 2018
cb14690
Add dock module
tassoevan Oct 29, 2018
19f540e
Make adjustments in tray and dock modules for MacOS
tassoevan Oct 29, 2018
bb26981
Deprecate showAlert parameter for tray and dock modules
tassoevan Oct 29, 2018
8e742c5
Fix wrong main window state
tassoevan Oct 29, 2018
16d4111
Refactor code
tassoevan Oct 29, 2018
380a4e8
Disable e2e tests in builds
tassoevan Oct 29, 2018
e2505ca
Set main window icon sizes order for KDE
tassoevan Oct 29, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,7 @@ before_install:

install:
- yarn
- yarn list

script:
- if [[ "$TRAVIS_PULL_REQUEST" != "false" ]]; then export CSC_IDENTITY_AUTO_DISCOVERY=false; fi
# e2e tests should be performed only on Linux
- |
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then
export DISPLAY=:99.0
sh -e /etc/init.d/xvfb start
sleep 3
yarn e2e
fi
- yarn release
15 changes: 9 additions & 6 deletions src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import idle from '@paulcbetts/system-idle-time';
import appData from './background/appData';
import autoUpdate from './background/autoUpdate';
import certificate from './background/certificate';
import { addServer, createMainWindow, getMainWindow } from './background/mainWindow';
import dock from './background/dock';
import { addServer, getMainWindow } from './background/mainWindow';
import menus from './background/menus';
import './background/screenshare';
import tray from './background/tray';
Expand All @@ -15,7 +16,8 @@ import i18n from './i18n/index.js';

export { default as showAboutDialog } from './background/aboutDialog';
export { default as remoteServers } from './background/servers';
export { certificate, menus, tray };
export { certificate, dock, menus, tray };


process.env.GOOGLE_API_KEY = 'AIzaSyADqUh_c1Qhji3Cp1NE43YrcpuPkmhXD-c';

Expand Down Expand Up @@ -78,18 +80,19 @@ if (process.platform === 'linux') {
app.disableHardwareAcceleration();
}

app.on('ready', () => {
app.on('ready', async() => {
unsetDefaultApplicationMenu();

appData.initialize();

createMainWindow();

getMainWindow().then((mainWindow) => certificate.initWindow(mainWindow));
const mainWindow = await getMainWindow();
certificate.initWindow(mainWindow);

autoUpdate();
});

ipcMain.on('getSystemIdleTime', (event) => {
event.returnValue = idle.getIdleTime();
});

process.on('unhandledRejection', console.error.bind(console));
83 changes: 83 additions & 0 deletions src/background/dock.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { app } from 'electron';
import { EventEmitter } from 'events';
import { getMainWindow } from './mainWindow';
import icon from './icon';


const getBadgeText = ({ badge: { title, count } }) => {
if (title === '•') {
return '•';
} else if (count > 0) {
return count > 9 ? '9+' : String(count);
} else if (title) {
return '!';
}
};

let state = {
badge: {
title: '',
count: 0,
},
status: 'online',
};

const instance = new (class Dock extends EventEmitter {});

const destroy = () => {
instance.removeAllListeners();
};

const update = async(previousState) => {
const mainWindow = await getMainWindow();
const badgeText = getBadgeText(state);

if (process.platform === 'win32') {
const image = badgeText ? await icon.render({
overlay: true,
size: 16,
badgeText,
}) : null;
mainWindow.setOverlayIcon(image, badgeText || '');

mainWindow.removeListener('show', update);
mainWindow.on('show', update);
}

if (process.platform === 'darwin') {
app.dock.setBadge(badgeText || '');
if (state.badge.count > 0 && previousState.badge.count === 0) {
app.dock.bounce();
}
}

if (process.platform === 'linux') {
mainWindow.setIcon(await icon.render({
badgeText,
size: {
win32: [256, 128, 64, 48, 32, 24, 16],
linux: 128,
}[process.platform],
}));
}

if (!mainWindow.isFocused()) {
mainWindow.flashFrame(state.badge.count > 0);
}

instance.emit('update');
};

const setState = (partialState) => {
const previousState = state;
state = {
...state,
...partialState,
};
update(previousState);
};

export default Object.assign(instance, {
destroy,
setState,
});
120 changes: 120 additions & 0 deletions src/background/icon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { BrowserWindow, nativeImage } from 'electron';
import jetpack from 'fs-jetpack';
import { whenReadyToShow } from './utils';

let rendererWindow = null;

const getRendererWindow = async() => {
if (!rendererWindow) {
rendererWindow = new BrowserWindow({ show: false });

const dataURL = `data:text/html,<!doctype html>
${ jetpack.read(`${ __dirname }/public/images/icon.svg`) }`;

rendererWindow.loadURL(dataURL);
await whenReadyToShow(rendererWindow);
}

return rendererWindow;
};

const renderInWindow = async(style) => {
const statusColors = {
offline: null,
away: 'yellow',
busy: 'red',
online: 'lime',
};

const create = ({ overlay, template, status, badgeText } = {}) => {
const svg = document.querySelector('#icon').cloneNode(true);

svg.querySelector('.logo .baloon').style.fill = template ? '#FFFFFF' : '#DB2323';
svg.querySelector('.logo .circles').style.fill = template ? '#FFFFFF' : '#DB2323';
svg.querySelector('.status .away').style.fill = template ? '#FFFFFF' : '#DB2323';
svg.querySelector('.status .busy').style.fill = template ? '#FFFFFF' : '#DB2323';

svg.querySelector('.logo .bubble').style.display = template ? 'none' : null;

svg.querySelector('.badge').style.display = (!template && badgeText) ? null : 'none';
svg.querySelector('.badge text').innerHTML = badgeText;

svg.querySelector('.logo .circles').style.display = (template && status && status !== 'online') ? 'none' : '';
svg.querySelector('.status circle').style.display = (template || !status) ? 'none' : null;
svg.querySelector('.status .away').style.display = (template && status === 'away') ? null : 'none';
svg.querySelector('.status .busy').style.display = (template && status === 'busy') ? null : 'none';
svg.querySelector('.status circle').style.fill = statusColors[status];

if (overlay) {
const overlaySVG = svg.cloneNode(true);
svg.remove();

overlaySVG.querySelector('.logo').remove();
overlaySVG.querySelector('.status').remove();
overlaySVG.setAttribute('viewBox', '96 -32 160 160');

return overlaySVG;
}

return svg;
};

const rasterize = async(svg, size) => {
const image = new Image();
image.src = `data:image/svg+xml,${ encodeURIComponent(svg.outerHTML) }`;
image.width = image.height = size;
await new Promise((resolve, reject) => {
image.onload = resolve;
image.onerror = reject;
});

const canvas = document.createElement('canvas');
canvas.width = canvas.height = size;

const ctx = canvas.getContext('2d');
ctx.drawImage(image, 0, 0);

return canvas.toDataURL('image/png');
};

const svg = create(style);
const pixelRatio = window.devicePixelRatio;
const sizes = Array.isArray(style.size) ? style.size : [style.size || 256];
const images = await Promise.all(sizes.map(async(size) => ({
dataURL: await rasterize(svg, size * pixelRatio),
size,
pixelRatio,
})));
svg.remove();
return images;
};

const render = async(style = {}) => {
const encodedArgs = JSON.stringify(style);
render.cache = render.cache || [];

if (render.cache[encodedArgs]) {
return render.cache[encodedArgs];
}

const rendererWindow = await getRendererWindow();
const jsCode = `(${ renderInWindow.toString() })(${ encodedArgs })`;
const images = await rendererWindow.webContents.executeJavaScript(jsCode);
const image = nativeImage.createEmpty();
for (const { dataURL, size, pixelRatio } of images) {
image.addRepresentation({
scaleFactor: pixelRatio,
width: size,
height: size,
dataURL,
});
}
image.setTemplateImage(style.template || false);
render.cache[encodedArgs] = image;

return image;
};

export default {
render,
};
Loading