Migrate app to TypeScript (#1637)
* Initial setup and migrated src/common * WIP * WIP * WIP * Main module basically finished * Renderer process migrated * Added CI step and some fixes * Fixed remainder of issues and added proper ESLint config * Fixed a couple issues * Progress! * Some more fixes * Fixed a test * Fix build step * PR feedback
This commit is contained in:
187
src/main/windows/mainWindow.ts
Normal file
187
src/main/windows/mainWindow.ts
Normal file
@@ -0,0 +1,187 @@
|
||||
// Copyright (c) 2016-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
import fs from 'fs';
|
||||
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
|
||||
import {app, BrowserWindow, BrowserWindowConstructorOptions, ipcMain} from 'electron';
|
||||
import log from 'electron-log';
|
||||
|
||||
import {CombinedConfig} from 'types/config';
|
||||
import {SavedWindowState} from 'types/mainWindow';
|
||||
|
||||
import {SELECT_NEXT_TAB, SELECT_PREVIOUS_TAB, GET_FULL_SCREEN_STATUS} from 'common/communication';
|
||||
|
||||
import * as Validator from '../Validator';
|
||||
import ContextMenu from '../contextMenu';
|
||||
import {getLocalPreload, getLocalURLString} from '../utils';
|
||||
|
||||
function saveWindowState(file: string, window: BrowserWindow) {
|
||||
const windowState: SavedWindowState = {
|
||||
...window.getBounds(),
|
||||
maximized: window.isMaximized(),
|
||||
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.
|
||||
log.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
function isFramelessWindow() {
|
||||
return os.platform() === 'darwin' || (os.platform() === 'win32' && os.release().startsWith('10'));
|
||||
}
|
||||
|
||||
function createMainWindow(config: CombinedConfig, options: {linuxAppIcon: string}) {
|
||||
const defaultWindowWidth = 1000;
|
||||
const defaultWindowHeight = 700;
|
||||
const minimumWindowWidth = 400;
|
||||
const minimumWindowHeight = 240;
|
||||
|
||||
// Create the browser window.
|
||||
const preload = getLocalPreload('mainWindow.js');
|
||||
const boundsInfoPath = path.join(app.getPath('userData'), 'bounds-info.json');
|
||||
let savedWindowState;
|
||||
try {
|
||||
savedWindowState = JSON.parse(fs.readFileSync(boundsInfoPath, 'utf-8'));
|
||||
savedWindowState = Validator.validateBoundsInfo(savedWindowState);
|
||||
if (!savedWindowState) {
|
||||
throw new Error('Provided bounds info file does not validate, using defaults instead.');
|
||||
}
|
||||
} catch (e) {
|
||||
// Follow Electron's defaults, except for window dimensions which targets 1024x768 screen resolution.
|
||||
savedWindowState = {width: defaultWindowWidth, height: defaultWindowHeight};
|
||||
}
|
||||
|
||||
const {maximized: windowIsMaximized} = savedWindowState;
|
||||
|
||||
const spellcheck = (typeof config.useSpellChecker === 'undefined' ? true : config.useSpellChecker);
|
||||
|
||||
const windowOptions: BrowserWindowConstructorOptions = Object.assign({}, savedWindowState, {
|
||||
title: app.name,
|
||||
fullscreenable: true,
|
||||
show: false, // don't start the window until it is ready and only if it isn't hidden
|
||||
paintWhenInitiallyHidden: true, // we want it to start painting to get info from the webapp
|
||||
minWidth: minimumWindowWidth,
|
||||
minHeight: minimumWindowHeight,
|
||||
frame: !isFramelessWindow(),
|
||||
fullscreen: savedWindowState.fullscreen,
|
||||
titleBarStyle: 'hidden' as const,
|
||||
trafficLightPosition: {x: 12, y: 24},
|
||||
backgroundColor: '#fff', // prevents blurry text: https://electronjs.org/docs/faq#the-font-looks-blurry-what-is-this-and-what-can-i-do
|
||||
webPreferences: {
|
||||
nodeIntegration: process.env.NODE_ENV === 'test',
|
||||
contextIsolation: process.env.NODE_ENV !== 'test',
|
||||
disableBlinkFeatures: 'Auxclick',
|
||||
preload,
|
||||
spellcheck,
|
||||
enableRemoteModule: process.env.NODE_ENV === 'test',
|
||||
},
|
||||
});
|
||||
|
||||
if (process.platform === 'linux') {
|
||||
windowOptions.icon = options.linuxAppIcon;
|
||||
}
|
||||
|
||||
const mainWindow = new BrowserWindow(windowOptions);
|
||||
mainWindow.setMenuBarVisibility(false);
|
||||
|
||||
try {
|
||||
ipcMain.handle(GET_FULL_SCREEN_STATUS, () => mainWindow.isFullScreen());
|
||||
} catch (e) {
|
||||
log.error('Tried to register second handler, skipping');
|
||||
}
|
||||
|
||||
const localURL = getLocalURLString('index.html');
|
||||
mainWindow.loadURL(localURL).catch(
|
||||
(reason) => {
|
||||
log.error(`Main window failed to load: ${reason}`);
|
||||
});
|
||||
mainWindow.once('ready-to-show', () => {
|
||||
mainWindow.webContents.zoomLevel = 0;
|
||||
|
||||
mainWindow.show();
|
||||
if (windowIsMaximized) {
|
||||
mainWindow.maximize();
|
||||
}
|
||||
});
|
||||
|
||||
mainWindow.once('show', () => {
|
||||
mainWindow.show();
|
||||
});
|
||||
|
||||
mainWindow.once('restore', () => {
|
||||
mainWindow.restore();
|
||||
});
|
||||
|
||||
// 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: BrowserWindow) {
|
||||
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':
|
||||
// need to leave fullscreen first, then hide the window
|
||||
if (mainWindow.isFullScreen()) {
|
||||
mainWindow.once('leave-full-screen', () => {
|
||||
app.hide();
|
||||
});
|
||||
mainWindow.setFullScreen(false);
|
||||
} else {
|
||||
app.hide();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Register keyboard shortcuts
|
||||
mainWindow.webContents.on('before-input-event', (event, input) => {
|
||||
// Add Alt+Cmd+(Right|Left) as alternative to switch between servers
|
||||
if (process.platform === 'darwin') {
|
||||
if (input.alt && input.meta) {
|
||||
if (input.key === 'ArrowRight') {
|
||||
mainWindow.webContents.send(SELECT_NEXT_TAB);
|
||||
}
|
||||
if (input.key === 'ArrowLeft') {
|
||||
mainWindow.webContents.send(SELECT_PREVIOUS_TAB);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const contextMenu = new ContextMenu({}, mainWindow);
|
||||
contextMenu.reload();
|
||||
|
||||
return mainWindow;
|
||||
}
|
||||
|
||||
export default createMainWindow;
|
Reference in New Issue
Block a user