2
0
mirror of https://github.com/frappe/books.git synced 2024-11-08 14:50:56 +00:00

refactor: shift main process stuff out of source

This commit is contained in:
18alantom 2022-03-16 15:19:48 +05:30
parent 761dd641bf
commit a596f5dcad
11 changed files with 444 additions and 3 deletions

153
main.ts Normal file
View File

@ -0,0 +1,153 @@
'use strict';
import {
app,
BrowserWindow,
BrowserWindowConstructorOptions,
protocol,
} from 'electron';
import Store from 'electron-store';
import { autoUpdater } from 'electron-updater';
import path from 'path';
import { createProtocol } from 'vue-cli-plugin-electron-builder/lib';
import { getMainWindowSize } from './main/helpers';
import registerAppLifecycleListeners from './main/registerAppLifecycleListeners';
import registerAutoUpdaterListeners from './main/registerAutoUpdaterListeners';
import registerIpcMainActionListeners from './main/registerIpcMainActionListeners';
import registerIpcMainMessageListeners from './main/registerIpcMainMessageListeners';
import registerProcessListeners from './main/registerProcessListeners';
import { IPC_CHANNELS } from './src/messages';
export class Main {
title: string = 'Frappe Books';
icon: string;
winURL: string = '';
isWebpackUrl: boolean = false;
checkedForUpdate = false;
mainWindow: BrowserWindow | null = null;
constructor() {
this.icon = this.isDevelopment
? path.resolve('./build/icon.png')
: path.join(__dirname, 'icons', '512x512.png');
protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } },
]);
if (this.isDevelopment) {
autoUpdater.logger = console;
}
Store.initRenderer();
this.registerListeners();
if (this.isMac && this.isDevelopment) {
app.dock.setIcon(this.icon);
}
}
get isDevelopment() {
return process.env.NODE_ENV !== 'production';
}
get isMac() {
return process.platform === 'darwin';
}
get isLinux() {
return process.platform === 'linux';
}
registerListeners() {
registerIpcMainMessageListeners(this);
registerIpcMainActionListeners(this);
registerAutoUpdaterListeners(this);
registerAppLifecycleListeners(this);
registerProcessListeners(this);
}
getOptions(): BrowserWindowConstructorOptions {
const { width, height } = getMainWindowSize();
const options: BrowserWindowConstructorOptions = {
vibrancy: 'sidebar',
transparent: this.isMac,
backgroundColor: '#80FFFFFF',
width,
height,
title: this.title,
webPreferences: {
contextIsolation: false, // TODO: Switch this off
nodeIntegration: process.env
.ELECTRON_NODE_INTEGRATION as unknown as boolean,
},
frame: this.isLinux,
resizable: true,
};
if (this.isDevelopment || this.isLinux) {
Object.assign(options, { icon: this.icon });
}
if (this.isLinux) {
Object.assign(options, {
icon: path.join(__dirname, '/icons/512x512.png'),
});
}
return options;
}
createWindow() {
const options = this.getOptions();
this.mainWindow = new BrowserWindow(options);
this.isWebpackUrl = !!process.env.WEBPACK_DEV_SERVER_URL;
if (this.isWebpackUrl) {
this.loadWebpackDevServerURL();
} else {
this.loadAppUrl();
}
this.setMainWindowListeners();
}
loadWebpackDevServerURL() {
// Load the url of the dev server if in development mode
this.winURL = process.env.WEBPACK_DEV_SERVER_URL as string;
this.mainWindow!.loadURL(this.winURL);
if (!process.env.IS_TEST) {
this.mainWindow!.webContents.openDevTools();
}
}
loadAppUrl() {
createProtocol('app');
// Load the index.html when not in development
this.winURL = 'app://./index.html';
this.mainWindow!.loadURL(this.winURL);
}
setMainWindowListeners() {
if (this.mainWindow === null) {
return;
}
this.mainWindow.on('closed', () => {
this.mainWindow = null;
});
this.mainWindow.webContents.on('did-finish-load', () => {
if (this.mainWindow === null) {
return;
}
this.mainWindow.webContents.send(IPC_CHANNELS.STORE_ON_WINDOW, {
appVersion: app.getVersion(),
});
});
}
}
export default new Main();

14
main/helpers.ts Normal file
View File

@ -0,0 +1,14 @@
import electron, { app } from 'electron';
export function getMainWindowSize() {
let height;
if (app.isReady()) {
const screen = electron.screen;
height = screen.getPrimaryDisplay().workAreaSize.height;
height = height > 907 ? 907 : height;
} else {
height = 907;
}
const width = Math.ceil(1.323 * height);
return { height, width };
}

View File

@ -0,0 +1,32 @@
import { app } from 'electron';
import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer';
import { Main } from '../main';
export default function registerAppLifecycleListeners(main: Main) {
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (main.mainWindow === null) {
main.createWindow();
}
});
app.on('ready', async () => {
if (main.isDevelopment && !process.env.IS_TEST) {
try {
await installExtension(VUEJS_DEVTOOLS);
} catch (e) {
console.error(
'Vue Devtools failed to install:',
(e as Error).toString()
);
}
}
main.createWindow();
});
}

View File

@ -0,0 +1,49 @@
import { autoUpdater, UpdateInfo } from 'electron-updater';
import { Main } from '../main';
import { IPC_CHANNELS } from '../src/messages';
export default function registerAutoUpdaterListeners(main: Main) {
autoUpdater.autoDownload = false;
autoUpdater.autoInstallOnAppQuit = false;
autoUpdater.on('checking-for-update', () => {
if (!main.checkedForUpdate) {
return;
}
main.mainWindow!.webContents.send(IPC_CHANNELS.CHECKING_FOR_UPDATE);
});
autoUpdater.on('update-available', (info: UpdateInfo) => {
if (!main.checkedForUpdate) {
main.checkedForUpdate = true;
}
main.mainWindow!.webContents.send(
IPC_CHANNELS.UPDATE_AVAILABLE,
info.version
);
});
autoUpdater.on('update-not-available', () => {
if (!main.checkedForUpdate) {
main.checkedForUpdate = true;
return;
}
main.mainWindow!.webContents.send(IPC_CHANNELS.UPDATE_NOT_AVAILABLE);
});
autoUpdater.on('update-downloaded', () => {
main.mainWindow!.webContents.send(IPC_CHANNELS.UPDATE_DOWNLOADED);
});
autoUpdater.on('error', (error) => {
if (!main.checkedForUpdate) {
main.checkedForUpdate = true;
return;
}
main.mainWindow!.webContents.send(IPC_CHANNELS.UPDATE_ERROR, error);
});
}

View File

@ -0,0 +1,115 @@
import { dialog, ipcMain } from 'electron';
import { autoUpdater } from 'electron-updater';
import fs from 'fs/promises';
import path from 'path';
import { Main } from '../main';
import { getUrlAndTokenString, sendError } from '../src/contactMothership';
import { getLanguageMap } from '../src/getLanguageMap';
import { IPC_ACTIONS } from '../src/messages';
import saveHtmlAsPdf from '../src/saveHtmlAsPdf';
import { getMainWindowSize } from './helpers';
export default function registerIpcMainActionListeners(main: Main) {
ipcMain.handle(IPC_ACTIONS.TOGGLE_MAXIMIZE_CURRENT_WINDOW, (event) => {
const maximizing = main.mainWindow!.isMaximized();
if (maximizing) {
main.mainWindow!.maximize();
} else {
main.mainWindow!.unmaximize();
}
return maximizing;
});
ipcMain.handle(IPC_ACTIONS.GET_OPEN_FILEPATH, async (event, options) => {
return await dialog.showOpenDialog(main.mainWindow!, options);
});
ipcMain.handle(IPC_ACTIONS.GET_SAVE_FILEPATH, async (event, options) => {
return await dialog.showSaveDialog(main.mainWindow!, options);
});
ipcMain.handle(IPC_ACTIONS.GET_PRIMARY_DISPLAY_SIZE, (event) => {
return getMainWindowSize();
});
ipcMain.handle(IPC_ACTIONS.GET_DIALOG_RESPONSE, async (event, options) => {
if (main.isDevelopment || main.isLinux) {
Object.assign(options, { icon: main.icon });
}
return await dialog.showMessageBox(main.mainWindow!, options);
});
ipcMain.handle(IPC_ACTIONS.SHOW_ERROR, async (event, { title, content }) => {
return await dialog.showErrorBox(title, content);
});
ipcMain.handle(
IPC_ACTIONS.SAVE_HTML_AS_PDF,
async (event, html, savePath) => {
return await saveHtmlAsPdf(html, savePath);
}
);
ipcMain.handle(IPC_ACTIONS.SAVE_DATA, async (event, data, savePath) => {
return await fs.writeFile(savePath, data, { encoding: 'utf-8' });
});
ipcMain.handle(IPC_ACTIONS.SEND_ERROR, (event, bodyJson) => {
sendError(bodyJson);
});
ipcMain.handle(IPC_ACTIONS.CHECK_FOR_UPDATES, (event, force) => {
if (!main.isDevelopment && !main.checkedForUpdate) {
autoUpdater.checkForUpdates();
} else if (force) {
autoUpdater.checkForUpdates();
}
});
ipcMain.handle(IPC_ACTIONS.GET_LANGUAGE_MAP, async (event, code) => {
const obj = { languageMap: {}, success: true, message: '' };
try {
obj.languageMap = await getLanguageMap(code, main.isDevelopment);
} catch (err) {
obj.success = false;
obj.message = (err as Error).message;
}
return obj;
});
ipcMain.handle(IPC_ACTIONS.GET_FILE, async (event, options) => {
const response = {
name: '',
filePath: '',
success: false,
data: Buffer.from('', 'utf-8'),
canceled: false,
};
const { filePaths, canceled } = await dialog.showOpenDialog(
main.mainWindow!,
options
);
response.filePath = filePaths?.[0];
response.canceled = canceled;
if (!response.filePath) {
return response;
}
response.success = true;
if (canceled) {
return response;
}
response.name = path.basename(response.filePath);
response.data = await fs.readFile(response.filePath);
return response;
});
ipcMain.handle(IPC_ACTIONS.GET_CREDS, async (event) => {
return await getUrlAndTokenString();
});
}

View File

@ -0,0 +1,54 @@
import { ipcMain, Menu, shell } from 'electron';
import { autoUpdater } from 'electron-updater';
import { Main } from '../main';
import { IPC_MESSAGES } from '../src/messages';
export default function registerIpcMainMessageListeners(main: Main) {
ipcMain.on(IPC_MESSAGES.OPEN_MENU, (event) => {
if (event.sender === null) {
return;
}
const menu = Menu.getApplicationMenu();
if (menu === null) {
return;
}
menu.popup({ window: main.mainWindow! });
});
ipcMain.on(IPC_MESSAGES.RELOAD_MAIN_WINDOW, () => {
main.mainWindow!.reload();
});
ipcMain.on(IPC_MESSAGES.RESIZE_MAIN_WINDOW, (event, size, resizable) => {
const [width, height] = size;
if (!width || !height) return;
main.mainWindow!.setSize(width, height);
main.mainWindow!.setResizable(resizable);
});
ipcMain.on(IPC_MESSAGES.CLOSE_CURRENT_WINDOW, (event) => {
main.mainWindow!.close();
});
ipcMain.on(IPC_MESSAGES.MINIMIZE_CURRENT_WINDOW, (event) => {
main.mainWindow!.minimize();
});
ipcMain.on(IPC_MESSAGES.OPEN_EXTERNAL, (event, link) => {
shell.openExternal(link);
});
ipcMain.on(IPC_MESSAGES.SHOW_ITEM_IN_FOLDER, (event, filePath) => {
return shell.showItemInFolder(filePath);
});
ipcMain.on(IPC_MESSAGES.DOWNLOAD_UPDATE, (event) => {
autoUpdater.downloadUpdate();
});
ipcMain.on(IPC_MESSAGES.INSTALL_UPDATE, (event) => {
autoUpdater.quitAndInstall(true, true);
});
}

View File

@ -0,0 +1,18 @@
import { app } from 'electron';
import { Main } from '../main';
export default function registerProcessListeners(main: Main) {
if (main.isDevelopment) {
if (process.platform === 'win32') {
process.on('message', (data) => {
if (data === 'graceful-exit') {
app.quit();
}
});
} else {
process.on('SIGTERM', () => {
app.quit();
});
}
}
}

View File

@ -16,7 +16,6 @@
"postuninstall": "electron-builder install-app-deps",
"script:translate": "node scripts/generateTranslations.js"
},
"main": "background.js",
"dependencies": {
"@popperjs/core": "^2.10.2",
"core-js": "^3.19.0",
@ -35,6 +34,7 @@
"devDependencies": {
"@babel/core": "^7.16.0",
"@babel/eslint-parser": "^7.16.0",
"@types/electron-devtools-installer": "^2.2.0",
"@types/lodash": "^4.14.179",
"@typescript-eslint/eslint-plugin": "^4.15.1",
"@typescript-eslint/parser": "^4.15.1",
@ -69,11 +69,11 @@
"singleQuote": true,
"trailingComma": "es5"
},
"engineStrict": true,
"engines": {
"node": ">=16.13.1 <17",
"yarn": "1.22.17"
},
"engineStrict": true,
"gitHooks": {
"pre-commit": "lint-staged"
},

View File

@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "esnext",
"target": "es2018",
"module": "esnext",
"strict": true,
"allowJs": true,

View File

@ -5,6 +5,7 @@ module.exports = {
pluginOptions: {
electronBuilder: {
nodeIntegration: true,
mainProcessFile: 'main.ts',
disableMainProcessTypescript: false,
mainProcessTypeChecking: true,
chainWebpackRendererProcess: (config) => {

View File

@ -1221,6 +1221,11 @@
dependencies:
"@types/ms" "*"
"@types/electron-devtools-installer@^2.2.0":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@types/electron-devtools-installer/-/electron-devtools-installer-2.2.1.tgz#27cd1e0a612ef070d980555ae32db5898649e813"
integrity sha512-V/gyryjHZi27/VeU5JtO89rqqq7w7d+Is0VYNLseARvR6ad7lXi8UCIJI9NraqNmd1NoGYedB1+tm5Q8UySF8A==
"@types/eslint-scope@^3.7.0":
version "3.7.2"
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.2.tgz#11e96a868c67acf65bf6f11d10bb89ea71d5e473"