Refactor main window code
This commit is contained in:
210
src/main.js
210
src/main.js
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
app,
|
app,
|
||||||
BrowserWindow,
|
|
||||||
Menu,
|
Menu,
|
||||||
Tray,
|
Tray,
|
||||||
ipcMain,
|
ipcMain,
|
||||||
@@ -20,7 +19,7 @@ process.on('uncaughtException', (error) => {
|
|||||||
console.error(error);
|
console.error(error);
|
||||||
});
|
});
|
||||||
|
|
||||||
var willAppQuit = false;
|
global.willAppQuit = false;
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
var cmd = process.argv[1];
|
var cmd = process.argv[1];
|
||||||
@@ -50,16 +49,16 @@ if (process.platform === 'win32') {
|
|||||||
|
|
||||||
app.setAppUserModelId('com.squirrel.mattermost.Mattermost'); // Use explicit AppUserModelID
|
app.setAppUserModelId('com.squirrel.mattermost.Mattermost'); // Use explicit AppUserModelID
|
||||||
if (require('electron-squirrel-startup')) {
|
if (require('electron-squirrel-startup')) {
|
||||||
willAppQuit = true;
|
global.willAppQuit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fs = require('fs');
|
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
var settings = require('./common/settings');
|
var settings = require('./common/settings');
|
||||||
const osVersion = require('./common/osVersion');
|
const osVersion = require('./common/osVersion');
|
||||||
var certificateStore = require('./main/certificateStore').load(path.resolve(app.getPath('userData'), 'certificate.json'));
|
var certificateStore = require('./main/certificateStore').load(path.resolve(app.getPath('userData'), 'certificate.json'));
|
||||||
|
const {createMainWindow} = require('./main/mainWindow');
|
||||||
const appMenu = require('./main/menus/app');
|
const appMenu = require('./main/menus/app');
|
||||||
const trayMenu = require('./main/menus/tray');
|
const trayMenu = require('./main/menus/tray');
|
||||||
const allowProtocolDialog = require('./main/allowProtocolDialog');
|
const allowProtocolDialog = require('./main/allowProtocolDialog');
|
||||||
@@ -205,35 +204,6 @@ function clearAppCache() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getValidWindowPosition(state) {
|
|
||||||
// Screen cannot be required before app is ready
|
|
||||||
const {screen} = require('electron');
|
|
||||||
|
|
||||||
// Check if the previous position is out of the viewable area
|
|
||||||
// (e.g. because the screen has been plugged off)
|
|
||||||
const displays = screen.getAllDisplays();
|
|
||||||
let minX = 0;
|
|
||||||
let maxX = 0;
|
|
||||||
let minY = 0;
|
|
||||||
let maxY = 0;
|
|
||||||
for (let i = 0; i < displays.length; i++) {
|
|
||||||
const display = displays[i];
|
|
||||||
maxX = Math.max(maxX, display.bounds.x + display.bounds.width);
|
|
||||||
maxY = Math.max(maxY, display.bounds.y + display.bounds.height);
|
|
||||||
minX = Math.min(minX, display.bounds.x);
|
|
||||||
minY = Math.min(minY, display.bounds.y);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.x > maxX || state.y > maxY || state.x < minX || state.y < minY) {
|
|
||||||
Reflect.deleteProperty(state, 'x');
|
|
||||||
Reflect.deleteProperty(state, 'y');
|
|
||||||
Reflect.deleteProperty(state, 'width');
|
|
||||||
Reflect.deleteProperty(state, 'height');
|
|
||||||
}
|
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quit when all windows are closed.
|
// Quit when all windows are closed.
|
||||||
app.on('window-all-closed', () => {
|
app.on('window-all-closed', () => {
|
||||||
// On OS X it is common for applications and their menu bar
|
// On OS X it is common for applications and their menu bar
|
||||||
@@ -253,7 +223,7 @@ app.on('before-quit', () => {
|
|||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
trayIcon.destroy();
|
trayIcon.destroy();
|
||||||
}
|
}
|
||||||
willAppQuit = true;
|
global.willAppQuit = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
|
app.on('certificate-error', (event, webContents, url, error, certificate, callback) => {
|
||||||
@@ -307,7 +277,7 @@ allowProtocolDialog.init(mainWindow);
|
|||||||
// This method will be called when Electron has finished
|
// This method will be called when Electron has finished
|
||||||
// initialization and is ready to create browser windows.
|
// initialization and is ready to create browser windows.
|
||||||
app.on('ready', () => {
|
app.on('ready', () => {
|
||||||
if (willAppQuit) {
|
if (global.willAppQuit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (global.isDev) {
|
if (global.isDev) {
|
||||||
@@ -316,6 +286,23 @@ app.on('ready', () => {
|
|||||||
catch((err) => console.log('An error occurred: ', err));
|
catch((err) => console.log('An error occurred: ', err));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mainWindow = createMainWindow(config, {
|
||||||
|
hideOnStartup,
|
||||||
|
linuxAppIcon: path.join(assetsDir, 'appicon.png')
|
||||||
|
});
|
||||||
|
mainWindow.on('closed', () => {
|
||||||
|
// Dereference the window object, usually you would store windows
|
||||||
|
// in an array if your app supports multi windows, this is the time
|
||||||
|
// when you should delete the corresponding element.
|
||||||
|
mainWindow = null;
|
||||||
|
});
|
||||||
|
mainWindow.on('unresponsive', () => {
|
||||||
|
console.log('The application has become unresponsive.');
|
||||||
|
});
|
||||||
|
mainWindow.webContents.on('crashed', () => {
|
||||||
|
console.log('The application has crashed.');
|
||||||
|
});
|
||||||
|
|
||||||
ipcMain.on('notified', () => {
|
ipcMain.on('notified', () => {
|
||||||
if (process.platform === 'win32' || process.platform === 'linux') {
|
if (process.platform === 'win32' || process.platform === 'linux') {
|
||||||
if (config.notifications.flashWindow === 2) {
|
if (config.notifications.flashWindow === 2) {
|
||||||
@@ -393,59 +380,35 @@ app.on('ready', () => {
|
|||||||
ipcMain.on('update-unread', (event, arg) => {
|
ipcMain.on('update-unread', (event, arg) => {
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
const overlay = arg.overlayDataURL ? nativeImage.createFromDataURL(arg.overlayDataURL) : null;
|
const overlay = arg.overlayDataURL ? nativeImage.createFromDataURL(arg.overlayDataURL) : null;
|
||||||
mainWindow.setOverlayIcon(overlay, arg.description);
|
if (mainWindow) {
|
||||||
|
mainWindow.setOverlayIcon(overlay, arg.description);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg.mentionCount > 0) {
|
if (trayIcon) {
|
||||||
trayIcon.setImage(trayImages.mention);
|
if (arg.mentionCount > 0) {
|
||||||
if (process.platform === 'darwin') {
|
trayIcon.setImage(trayImages.mention);
|
||||||
trayIcon.setPressedImage(trayImages.clicked.mention);
|
if (process.platform === 'darwin') {
|
||||||
|
trayIcon.setPressedImage(trayImages.clicked.mention);
|
||||||
|
}
|
||||||
|
trayIcon.setToolTip(arg.mentionCount + ' unread mentions');
|
||||||
|
} else if (arg.unreadCount > 0) {
|
||||||
|
trayIcon.setImage(trayImages.unread);
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
trayIcon.setPressedImage(trayImages.clicked.unread);
|
||||||
|
}
|
||||||
|
trayIcon.setToolTip(arg.unreadCount + ' unread channels');
|
||||||
|
} else {
|
||||||
|
trayIcon.setImage(trayImages.normal);
|
||||||
|
if (process.platform === 'darwin') {
|
||||||
|
trayIcon.setPressedImage(trayImages.clicked.normal);
|
||||||
|
}
|
||||||
|
trayIcon.setToolTip(app.getName());
|
||||||
}
|
}
|
||||||
trayIcon.setToolTip(arg.mentionCount + ' unread mentions');
|
|
||||||
} else if (arg.unreadCount > 0) {
|
|
||||||
trayIcon.setImage(trayImages.unread);
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
trayIcon.setPressedImage(trayImages.clicked.unread);
|
|
||||||
}
|
|
||||||
trayIcon.setToolTip(arg.unreadCount + ' unread channels');
|
|
||||||
} else {
|
|
||||||
trayIcon.setImage(trayImages.normal);
|
|
||||||
if (process.platform === 'darwin') {
|
|
||||||
trayIcon.setPressedImage(trayImages.clicked.normal);
|
|
||||||
}
|
|
||||||
trayIcon.setToolTip(app.getName());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the browser window.
|
|
||||||
var boundsInfoPath = path.resolve(app.getPath('userData'), 'bounds-info.json');
|
|
||||||
var windowOptions;
|
|
||||||
try {
|
|
||||||
windowOptions = getValidWindowPosition(JSON.parse(fs.readFileSync(boundsInfoPath, 'utf-8')));
|
|
||||||
} catch (e) {
|
|
||||||
// Follow Electron's defaults, except for window dimensions which targets 1024x768 screen resolution.
|
|
||||||
windowOptions = {width: 1000, height: 700};
|
|
||||||
}
|
|
||||||
if (process.platform === 'linux') {
|
|
||||||
windowOptions.icon = path.resolve(assetsDir, 'appicon.png');
|
|
||||||
}
|
|
||||||
Object.assign(windowOptions, {
|
|
||||||
title: app.getName(),
|
|
||||||
fullscreenable: true,
|
|
||||||
show: false,
|
|
||||||
minWidth: 400,
|
|
||||||
minHeight: 240
|
|
||||||
});
|
|
||||||
mainWindow = new BrowserWindow(windowOptions);
|
|
||||||
mainWindow.once('ready-to-show', () => {
|
|
||||||
if (process.platform !== 'darwin') {
|
|
||||||
mainWindow.show();
|
|
||||||
} else if (hideOnStartup !== true) {
|
|
||||||
mainWindow.show();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (process.platform === 'darwin') {
|
if (process.platform === 'darwin') {
|
||||||
session.defaultSession.on('will-download', (event, item) => {
|
session.defaultSession.on('will-download', (event, item) => {
|
||||||
var filename = item.getFilename();
|
var filename = item.getFilename();
|
||||||
@@ -462,35 +425,6 @@ app.on('ready', () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mainWindow.webContents.on('crashed', () => {
|
|
||||||
console.log('The application has crashed.');
|
|
||||||
});
|
|
||||||
|
|
||||||
mainWindow.webContents.on('will-attach-webview', (event, webPreferences) => {
|
|
||||||
webPreferences.nodeIntegration = false;
|
|
||||||
});
|
|
||||||
|
|
||||||
mainWindow.on('unresponsive', () => {
|
|
||||||
console.log('The application has become unresponsive.');
|
|
||||||
});
|
|
||||||
|
|
||||||
if (hideOnStartup) {
|
|
||||||
if (windowOptions.maximized) {
|
|
||||||
mainWindow.maximize();
|
|
||||||
}
|
|
||||||
|
|
||||||
// on MacOS, the window is already hidden until 'ready-to-show'
|
|
||||||
if (process.platform !== 'darwin') {
|
|
||||||
mainWindow.minimize();
|
|
||||||
}
|
|
||||||
} else if (windowOptions.maximized) {
|
|
||||||
mainWindow.maximize();
|
|
||||||
}
|
|
||||||
|
|
||||||
// and load the index.html of the app.
|
|
||||||
const indexURL = global.isDev ? 'http://localhost:8080/browser/index.html' : `file://${app.getAppPath()}/browser/index.html`;
|
|
||||||
mainWindow.loadURL(indexURL);
|
|
||||||
|
|
||||||
// Set application menu
|
// Set application menu
|
||||||
ipcMain.on('update-menu', (event, configData) => {
|
ipcMain.on('update-menu', (event, configData) => {
|
||||||
var aMenu = appMenu.createMenu(mainWindow, configData, global.isDev);
|
var aMenu = appMenu.createMenu(mainWindow, configData, global.isDev);
|
||||||
@@ -515,60 +449,4 @@ app.on('ready', () => {
|
|||||||
|
|
||||||
// Open the DevTools.
|
// Open the DevTools.
|
||||||
// mainWindow.openDevTools();
|
// mainWindow.openDevTools();
|
||||||
|
|
||||||
function saveWindowState(file, window) {
|
|
||||||
var windowState = window.getBounds();
|
|
||||||
windowState.maximized = window.isMaximized();
|
|
||||||
windowState.fullscreen = window.isFullScreen();
|
|
||||||
try {
|
|
||||||
fs.writeFileSync(boundsInfoPath, JSON.stringify(windowState));
|
|
||||||
} catch (e) {
|
|
||||||
// [Linux] error happens only when the window state is changed before the config dir is creatied.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mainWindow.on('close', (event) => {
|
|
||||||
if (willAppQuit) { // when [Ctrl|Cmd]+Q
|
|
||||||
saveWindowState(boundsInfoPath, mainWindow);
|
|
||||||
} else { // Minimize or hide the window for close button.
|
|
||||||
event.preventDefault();
|
|
||||||
function hideWindow(window) {
|
|
||||||
window.blur(); // To move focus to the next top-level window in Windows
|
|
||||||
window.hide();
|
|
||||||
}
|
|
||||||
switch (process.platform) {
|
|
||||||
case 'win32':
|
|
||||||
hideWindow(mainWindow);
|
|
||||||
break;
|
|
||||||
case 'linux':
|
|
||||||
if (config.minimizeToTray) {
|
|
||||||
hideWindow(mainWindow);
|
|
||||||
} else {
|
|
||||||
mainWindow.minimize();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'darwin':
|
|
||||||
hideWindow(mainWindow);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// App should save bounds when a window is closed.
|
|
||||||
// However, 'close' is not fired in some situations(shutdown, ctrl+c)
|
|
||||||
// because main process is killed in such situations.
|
|
||||||
// 'blur' event was effective in order to avoid this.
|
|
||||||
// Ideally, app should detect that OS is shutting down.
|
|
||||||
mainWindow.on('blur', () => {
|
|
||||||
saveWindowState(boundsInfoPath, mainWindow);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Emitted when the window is closed.
|
|
||||||
mainWindow.on('closed', () => {
|
|
||||||
// Dereference the window object, usually you would store windows
|
|
||||||
// in an array if your app supports multi windows, this is the time
|
|
||||||
// when you should delete the corresponding element.
|
|
||||||
mainWindow = null;
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
141
src/main/mainWindow.js
Normal file
141
src/main/mainWindow.js
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
const {app, BrowserWindow} = require('electron');
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
function saveWindowState(file, window) {
|
||||||
|
var windowState = window.getBounds();
|
||||||
|
windowState.maximized = window.isMaximized();
|
||||||
|
windowState.fullscreen = window.isFullScreen();
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(file, JSON.stringify(windowState));
|
||||||
|
} catch (e) {
|
||||||
|
// [Linux] error happens only when the window state is changed before the config dir is created.
|
||||||
|
console.log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getValidWindowPosition(state) {
|
||||||
|
// Screen cannot be required before app is ready
|
||||||
|
const {screen} = require('electron'); // eslint-disable-line global-require
|
||||||
|
|
||||||
|
// Check if the previous position is out of the viewable area
|
||||||
|
// (e.g. because the screen has been plugged off)
|
||||||
|
const displays = screen.getAllDisplays();
|
||||||
|
let minX = 0;
|
||||||
|
let maxX = 0;
|
||||||
|
let minY = 0;
|
||||||
|
let maxY = 0;
|
||||||
|
for (let i = 0; i < displays.length; i++) {
|
||||||
|
const display = displays[i];
|
||||||
|
maxX = Math.max(maxX, display.bounds.x + display.bounds.width);
|
||||||
|
maxY = Math.max(maxY, display.bounds.y + display.bounds.height);
|
||||||
|
minX = Math.min(minX, display.bounds.x);
|
||||||
|
minY = Math.min(minY, display.bounds.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.x > maxX || state.y > maxY || state.x < minX || state.y < minY) {
|
||||||
|
Reflect.deleteProperty(state, 'x');
|
||||||
|
Reflect.deleteProperty(state, 'y');
|
||||||
|
Reflect.deleteProperty(state, 'width');
|
||||||
|
Reflect.deleteProperty(state, 'height');
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function createMainWindow(config, options) {
|
||||||
|
const defaultWindowWidth = 1000;
|
||||||
|
const defaultWindowHeight = 700;
|
||||||
|
const minimumWindowWidth = 400;
|
||||||
|
const minimumWindowHeight = 240;
|
||||||
|
|
||||||
|
// Create the browser window.
|
||||||
|
const boundsInfoPath = path.join(app.getPath('userData'), 'bounds-info.json');
|
||||||
|
var windowOptions;
|
||||||
|
try {
|
||||||
|
windowOptions = getValidWindowPosition(JSON.parse(fs.readFileSync(boundsInfoPath, 'utf-8')));
|
||||||
|
} catch (e) {
|
||||||
|
// Follow Electron's defaults, except for window dimensions which targets 1024x768 screen resolution.
|
||||||
|
windowOptions = {width: defaultWindowWidth, height: defaultWindowHeight};
|
||||||
|
}
|
||||||
|
if (process.platform === 'linux') {
|
||||||
|
windowOptions.icon = options.linuxAppIcon;
|
||||||
|
}
|
||||||
|
Object.assign(windowOptions, {
|
||||||
|
title: app.getName(),
|
||||||
|
fullscreenable: true,
|
||||||
|
show: false,
|
||||||
|
minWidth: minimumWindowWidth,
|
||||||
|
minHeight: minimumWindowHeight
|
||||||
|
});
|
||||||
|
|
||||||
|
const mainWindow = new BrowserWindow(windowOptions);
|
||||||
|
if (options.hideOnStartup) {
|
||||||
|
if (windowOptions.maximized) {
|
||||||
|
mainWindow.maximize();
|
||||||
|
}
|
||||||
|
|
||||||
|
// on MacOS, the window is already hidden until 'ready-to-show'
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
mainWindow.minimize();
|
||||||
|
}
|
||||||
|
} else if (windowOptions.maximized) {
|
||||||
|
mainWindow.maximize();
|
||||||
|
}
|
||||||
|
|
||||||
|
mainWindow.webContents.on('will-attach-webview', (event, webPreferences) => {
|
||||||
|
webPreferences.nodeIntegration = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
const indexURL = global.isDev ? 'http://localhost:8080/browser/index.html' : `file://${app.getAppPath()}/browser/index.html`;
|
||||||
|
mainWindow.loadURL(indexURL);
|
||||||
|
|
||||||
|
mainWindow.once('ready-to-show', () => {
|
||||||
|
if (process.platform !== 'darwin') {
|
||||||
|
mainWindow.show();
|
||||||
|
} else if (options.hideOnStartup !== true) {
|
||||||
|
mainWindow.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// App should save bounds when a window is closed.
|
||||||
|
// However, 'close' is not fired in some situations(shutdown, ctrl+c)
|
||||||
|
// because main process is killed in such situations.
|
||||||
|
// 'blur' event was effective in order to avoid this.
|
||||||
|
// Ideally, app should detect that OS is shutting down.
|
||||||
|
mainWindow.on('blur', () => {
|
||||||
|
saveWindowState(boundsInfoPath, mainWindow);
|
||||||
|
});
|
||||||
|
|
||||||
|
mainWindow.on('close', (event) => {
|
||||||
|
if (global.willAppQuit) { // when [Ctrl|Cmd]+Q
|
||||||
|
saveWindowState(boundsInfoPath, mainWindow);
|
||||||
|
} else { // Minimize or hide the window for close button.
|
||||||
|
event.preventDefault();
|
||||||
|
function hideWindow(window) {
|
||||||
|
window.blur(); // To move focus to the next top-level window in Windows
|
||||||
|
window.hide();
|
||||||
|
}
|
||||||
|
switch (process.platform) {
|
||||||
|
case 'win32':
|
||||||
|
hideWindow(mainWindow);
|
||||||
|
break;
|
||||||
|
case 'linux':
|
||||||
|
if (config.minimizeToTray) {
|
||||||
|
hideWindow(mainWindow);
|
||||||
|
} else {
|
||||||
|
mainWindow.minimize();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'darwin':
|
||||||
|
hideWindow(mainWindow);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return mainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {createMainWindow};
|
Reference in New Issue
Block a user