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:
Devin Binnie
2021-06-28 09:51:23 -04:00
committed by GitHub
parent 422673a740
commit 1b3d0eac8f
115 changed files with 16246 additions and 9921 deletions

View 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;