:electron: Electron package Typescript starting point (#2880)

This commit is contained in:
Michael Clark
2024-07-03 21:28:04 +01:00
committed by GitHub
parent a67c969189
commit 31eb00a155
8 changed files with 181 additions and 66 deletions

1
.gitignore vendored
View File

@@ -21,6 +21,7 @@ packages/api/dist
packages/api/@types
packages/crdt/dist
packages/desktop-electron/client-build
packages/desktop-electron/build
packages/desktop-electron/.electron-symbols
packages/desktop-electron/dist
packages/desktop-electron/loot-core

View File

@@ -2,12 +2,13 @@
ROOT=`dirname $0`/..
rm -rf ${ROOT}/client-build
cp -r ${ROOT}/../desktop-client/build ${ROOT}/client-build
rm -rf ${ROOT}/build
mkdir -p ${ROOT}/build
cp -r ${ROOT}/../desktop-client/build ${ROOT}/build/client-build
# Remove the embedded backend for the browser version. Will improve
# this process
rm -rf ${ROOT}/client-build/data
rm -rf ${ROOT}/client-build/*kcab.*
rm -rf ${ROOT}/client-build/*.wasm
rm -rf ${ROOT}/client-build/*.map
rm -rf ${ROOT}/build/client-build/data
rm -rf ${ROOT}/build/client-build/*kcab.*
rm -rf ${ROOT}/build/client-build/*.wasm
rm -rf ${ROOT}/build/client-build/*.map

View File

@@ -1,11 +1,8 @@
/* eslint-disable import/order */
// (I have no idea why the imports are like this. Not touching them.)
const isDev = require('electron-is-dev');
const fs = require('fs');
import fs from 'fs';
import NodeModule from 'module';
import path from 'path';
require('module').globalPaths.push(__dirname + '/..');
const {
import {
app,
ipcMain,
BrowserWindow,
@@ -14,8 +11,28 @@ const {
shell,
protocol,
utilityProcess,
} = require('electron');
const promiseRetry = require('promise-retry');
UtilityProcess,
} from 'electron';
import isDev from 'electron-is-dev';
import promiseRetry from 'promise-retry';
import about from './about';
import getMenu from './menu';
import updater from './updater';
import {
get as getWindowState,
listen as listenToWindowState,
} from './window-state';
import './setRequireHook';
import './security';
const Module: typeof NodeModule & { globalPaths: string[] } =
// eslint-disable-next-line @typescript-eslint/no-explicit-any
NodeModule as unknown as any;
Module.globalPaths.push(__dirname + '/..');
// This allows relative URLs to be resolved to app:// which makes
// local assets load correctly
@@ -25,16 +42,6 @@ protocol.registerSchemesAsPrivileged([
global.fetch = require('node-fetch');
const about = require('./about');
const getMenu = require('./menu');
const updater = require('./updater');
require('./security');
const path = require('path');
require('./setRequireHook');
if (!isDev || !process.env.ACTUAL_DOCUMENT_DIR) {
process.env.ACTUAL_DOCUMENT_DIR = app.getPath('documents');
}
@@ -43,15 +50,12 @@ if (!isDev || !process.env.ACTUAL_DATA_DIR) {
process.env.ACTUAL_DATA_DIR = app.getPath('userData');
}
// eslint-disable-next-line import/extensions
const WindowState = require('./window-state.js');
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let clientWin;
let serverProcess;
let clientWin: BrowserWindow | null;
let serverProcess: UtilityProcess | null;
updater.onEvent((type, data) => {
updater.onEvent((type: string, data: Record<string, string> | string) => {
// Notify both the app and the about window
if (clientWin) {
clientWin.webContents.send(type, data);
@@ -99,7 +103,7 @@ function createBackgroundProcess() {
}
async function createWindow() {
const windowState = await WindowState.get();
const windowState = await getWindowState();
// Create the browser window.
const win = new BrowserWindow({
@@ -113,7 +117,6 @@ async function createWindow() {
nodeIntegrationInWorker: false,
nodeIntegrationInSubFrames: false,
contextIsolation: true,
enableRemoteModule: false,
preload: __dirname + '/preload.js',
},
});
@@ -123,7 +126,7 @@ async function createWindow() {
win.webContents.openDevTools();
}
const unlistenToState = WindowState.listen(win, windowState);
const unlistenToState = listenToWindowState(win, windowState);
if (isDev) {
win.loadURL(`file://${__dirname}/loading.html`);
@@ -148,9 +151,11 @@ async function createWindow() {
});
win.on('focus', async () => {
const url = clientWin.webContents.getURL();
if (url.includes('app://') || url.includes('localhost:')) {
clientWin.webContents.executeJavaScript('__actionsForMenu.focused()');
if (clientWin) {
const url = clientWin.webContents.getURL();
if (url.includes('app://') || url.includes('localhost:')) {
clientWin.webContents.executeJavaScript('__actionsForMenu.focused()');
}
}
});
@@ -183,29 +188,28 @@ async function createWindow() {
clientWin = win;
}
function isExternalUrl(url) {
function isExternalUrl(url: string) {
return !url.includes('localhost:') && !url.includes('app://');
}
function updateMenu(budgetId) {
function updateMenu(budgetId?: string) {
const isBudgetOpen = !!budgetId;
const menu = getMenu(isDev, createWindow);
const menu = getMenu(isDev, createWindow, budgetId);
const file = menu.items.filter(item => item.label === 'File')[0];
const fileItems = file.submenu.items;
const fileItems = file.submenu?.items || [];
fileItems
.filter(item => item.label === 'Load Backup...')
.forEach(item => {
item.enabled = isBudgetOpen;
item.budgetId = budgetId;
});
const tools = menu.items.filter(item => item.label === 'Tools')[0];
tools.submenu.items.forEach(item => {
tools.submenu?.items.forEach(item => {
item.enabled = isBudgetOpen;
});
const edit = menu.items.filter(item => item.label === 'Edit')[0];
const editItems = edit.submenu.items;
const editItems = edit.submenu?.items || [];
editItems
.filter(item => item.label === 'Undo' || item.label === 'Redo')
.map(item => (item.enabled = isBudgetOpen));
@@ -312,7 +316,7 @@ ipcMain.handle(
async (event, { title, defaultPath, fileContents }) => {
const fileLocation = await dialog.showSaveDialog({ title, defaultPath });
return new Promise((resolve, reject) => {
return new Promise<void>((resolve, reject) => {
if (fileLocation) {
fs.writeFile(fileLocation.filePath, fileContents, error => {
return reject(error);
@@ -344,8 +348,9 @@ ipcMain.on('screenshot', () => {
const width = 1100;
// This is for the main screenshot inside the frame
clientWin.setSize(width, Math.floor(width * (427 / 623)));
// clientWin.setSize(width, Math.floor(width * (495 / 700)));
if (clientWin) {
clientWin.setSize(width, Math.floor(width * (427 / 623)));
}
}
});
@@ -366,14 +371,15 @@ ipcMain.on('apply-update', () => {
updater.apply();
});
ipcMain.on('update-menu', (event, budgetId) => {
ipcMain.on('update-menu', (event, budgetId?: string) => {
updateMenu(budgetId);
});
ipcMain.on('set-theme', theme => {
const obj = { theme };
clientWin.webContents.executeJavaScript(
`window.__actionsForMenu && window.__actionsForMenu.saveGlobalPrefs(${obj})`,
);
if (clientWin) {
clientWin.webContents.executeJavaScript(
`window.__actionsForMenu && window.__actionsForMenu.saveGlobalPrefs(${obj})`,
);
}
});

View File

@@ -1,6 +1,6 @@
const { Menu, ipcMain, app, shell } = require('electron');
function getMenu(isDev, createWindow) {
function getMenu(isDev, createWindow, budgetId) {
const template = [
{
label: 'File',
@@ -9,10 +9,10 @@ function getMenu(isDev, createWindow) {
label: 'Load Backup...',
enabled: false,
click(item, focusedWindow) {
if (focusedWindow) {
if (focusedWindow && budgetId) {
if (focusedWindow.webContents.getTitle() === 'Actual') {
focusedWindow.webContents.executeJavaScript(
`__actionsForMenu.replaceModal('load-backup', { budgetId: '${item.budgetId}' })`,
`__actionsForMenu.replaceModal('load-backup', { budgetId: '${budgetId}' })`,
);
}
}

View File

@@ -7,10 +7,12 @@
"scripts": {
"clean": "rm -rf dist",
"update-client": "bin/update-client",
"build": "electron-builder",
"watch": "cross-env ACTUAL_DOCUMENT_DIR=\"../../data\" ACTUAL_DATA_DIR=\"../../data\" electron ."
"build": "yarn build:dist && electron-builder",
"build:dist": "tsc --p tsconfig.dist.json && yarn copy-static-html",
"copy-static-html": "copyfiles --exclude 'build/**/*' **/*.html icons/**/* build",
"watch": "yarn build:dist && cross-env ACTUAL_DOCUMENT_DIR=\"../../data\" ACTUAL_DATA_DIR=\"../../data\" electron ."
},
"main": "index.js",
"main": "build/index.js",
"build": {
"appId": "com.actualbudget.actual",
"files": [
@@ -19,7 +21,8 @@
"!**/*.js.map",
"!node_modules/@jlongster/sql.js",
"!node_modules/absurd-sql",
"!node_modules/better-sqlite3/{benchmark,src,bin,docs,deps,build/Release/obj,build/Release/sqlite3.a,build/Release/test_extension.node}"
"!node_modules/better-sqlite3/{benchmark,src,bin,docs,deps,build/Release/obj,build/Release/sqlite3.a,build/Release/test_extension.node}",
"build"
],
"publish": {
"provider": "github",
@@ -63,8 +66,11 @@
"devDependencies": {
"@electron/notarize": "2.2.0",
"@electron/rebuild": "3.6.0",
"@types/copyfiles": "^2",
"copyfiles": "^2.4.1",
"cross-env": "^7.0.3",
"electron": "30.0.6",
"electron-builder": "24.13.3"
"electron-builder": "24.13.3",
"typescript": "^5.0.2"
}
}

View File

@@ -0,0 +1,14 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
// Using ES2021 because thats the newest version where
// the latest Node 16.x release supports all of the features
"target": "ES2021",
"module": "CommonJS",
"noEmit": false,
"declaration": true,
"outDir": "build",
},
"include": ["."],
"exclude": ["**/node_modules/*", "build/**/*"]
}

View File

@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [MikesGlitch]
---
Refactoring desktop-electron package to use typescript

View File

@@ -5443,6 +5443,13 @@ __metadata:
languageName: node
linkType: hard
"@types/copyfiles@npm:^2":
version: 2.4.4
resolution: "@types/copyfiles@npm:2.4.4"
checksum: 0513199240828feda5f6ed04c69d6a642c47e6ab66b81214716807f948ed3e865e9b3d2b69f75cbcc6fbe2154630755c47ca473b3913f0a831179366c709a8cc
languageName: node
linkType: hard
"@types/d3-array@npm:^3.0.3":
version: 3.0.4
resolution: "@types/d3-array@npm:3.0.4"
@@ -7999,6 +8006,24 @@ __metadata:
languageName: node
linkType: hard
"copyfiles@npm:^2.4.1":
version: 2.4.1
resolution: "copyfiles@npm:2.4.1"
dependencies:
glob: "npm:^7.0.5"
minimatch: "npm:^3.0.3"
mkdirp: "npm:^1.0.4"
noms: "npm:0.0.0"
through2: "npm:^2.0.1"
untildify: "npm:^4.0.0"
yargs: "npm:^16.1.0"
bin:
copyfiles: copyfiles
copyup: copyfiles
checksum: 17070f88cbeaf62a9355341cb2521bacd48069e1ac8e7f95a3f69c848c53646f16ff0f94807a789e0f3eedc11407ec8d3980a13ab62e2add6ef81d0a5900fd85
languageName: node
linkType: hard
"core-js-compat@npm:^3.31.0, core-js-compat@npm:^3.34.0":
version: 3.36.0
resolution: "core-js-compat@npm:3.36.0"
@@ -8655,6 +8680,8 @@ __metadata:
dependencies:
"@electron/notarize": "npm:2.2.0"
"@electron/rebuild": "npm:3.6.0"
"@types/copyfiles": "npm:^2"
copyfiles: "npm:^2.4.1"
cross-env: "npm:^7.0.3"
electron: "npm:30.0.6"
electron-builder: "npm:24.13.3"
@@ -8664,6 +8691,7 @@ __metadata:
loot-core: "npm:*"
node-fetch: "npm:^2.7.0"
promise-retry: "npm:^2.0.1"
typescript: "npm:^5.0.2"
languageName: unknown
linkType: soft
@@ -10682,7 +10710,7 @@ __metadata:
languageName: node
linkType: hard
"glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6":
"glob@npm:^7.0.5, glob@npm:^7.1.1, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6":
version: 7.2.3
resolution: "glob@npm:7.2.3"
dependencies:
@@ -11206,7 +11234,7 @@ __metadata:
languageName: node
linkType: hard
"inherits@npm:2, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3, inherits@npm:~2.0.4":
"inherits@npm:2, inherits@npm:^2.0.1, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.1, inherits@npm:~2.0.3, inherits@npm:~2.0.4":
version: 2.0.4
resolution: "inherits@npm:2.0.4"
checksum: cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521
@@ -11771,6 +11799,13 @@ __metadata:
languageName: node
linkType: hard
"isarray@npm:0.0.1":
version: 0.0.1
resolution: "isarray@npm:0.0.1"
checksum: 49191f1425681df4a18c2f0f93db3adb85573bcdd6a4482539d98eac9e705d8961317b01175627e860516a2fc45f8f9302db26e5a380a97a520e272e2a40a8d4
languageName: node
linkType: hard
"isarray@npm:^2.0.5":
version: 2.0.5
resolution: "isarray@npm:2.0.5"
@@ -13976,7 +14011,7 @@ __metadata:
languageName: node
linkType: hard
"minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2":
"minimatch@npm:^3.0.3, minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2":
version: 3.1.2
resolution: "minimatch@npm:3.1.2"
dependencies:
@@ -14401,6 +14436,16 @@ __metadata:
languageName: node
linkType: hard
"noms@npm:0.0.0":
version: 0.0.0
resolution: "noms@npm:0.0.0"
dependencies:
inherits: "npm:^2.0.1"
readable-stream: "npm:~1.0.31"
checksum: a05f056dabf764c86472b6b5aad10455f3adcb6971f366cdf36a72b559b29310a940e316bca30802f2804fdd41707941366224f4cba80c4f53071512245bf200
languageName: node
linkType: hard
"nopt@npm:^6.0.0":
version: 6.0.0
resolution: "nopt@npm:6.0.0"
@@ -15893,7 +15938,7 @@ __metadata:
languageName: node
linkType: hard
"readable-stream@npm:^2.3.0, readable-stream@npm:^2.3.5":
"readable-stream@npm:^2.3.0, readable-stream@npm:^2.3.5, readable-stream@npm:~2.3.6":
version: 2.3.8
resolution: "readable-stream@npm:2.3.8"
dependencies:
@@ -15919,6 +15964,18 @@ __metadata:
languageName: node
linkType: hard
"readable-stream@npm:~1.0.31":
version: 1.0.34
resolution: "readable-stream@npm:1.0.34"
dependencies:
core-util-is: "npm:~1.0.0"
inherits: "npm:~2.0.1"
isarray: "npm:0.0.1"
string_decoder: "npm:~0.10.x"
checksum: 20537fca5a8ffd4af0f483be1cce0e981ed8cbb1087e0c762e2e92ae77f1005627272cebed8422f28047b465056aa1961fefd24baf532ca6a3616afea6811ae0
languageName: node
linkType: hard
"readdirp@npm:~3.6.0":
version: 3.6.0
resolution: "readdirp@npm:3.6.0"
@@ -17155,6 +17212,13 @@ __metadata:
languageName: node
linkType: hard
"string_decoder@npm:~0.10.x":
version: 0.10.31
resolution: "string_decoder@npm:0.10.31"
checksum: cc43e6b1340d4c7843da0e37d4c87a4084c2342fc99dcf6563c3ec273bb082f0cbd4ebf25d5da19b04fb16400d393885fda830be5128e1c416c73b5a6165f175
languageName: node
linkType: hard
"string_decoder@npm:~1.1.1":
version: 1.1.1
resolution: "string_decoder@npm:1.1.1"
@@ -17603,6 +17667,16 @@ __metadata:
languageName: node
linkType: hard
"through2@npm:^2.0.1":
version: 2.0.5
resolution: "through2@npm:2.0.5"
dependencies:
readable-stream: "npm:~2.3.6"
xtend: "npm:~4.0.1"
checksum: cd71f7dcdc7a8204fea003a14a433ef99384b7d4e31f5497e1f9f622b3cf3be3691f908455f98723bdc80922a53af7fa10c3b7abbe51c6fd3d536dbc7850e2c4
languageName: node
linkType: hard
"through@npm:^2.3.8":
version: 2.3.8
resolution: "through@npm:2.3.8"
@@ -18335,6 +18409,13 @@ __metadata:
languageName: node
linkType: hard
"untildify@npm:^4.0.0":
version: 4.0.0
resolution: "untildify@npm:4.0.0"
checksum: 39ced9c418a74f73f0a56e1ba4634b4d959422dff61f4c72a8e39f60b99380c1b45ed776fbaa0a4101b157e4310d873ad7d114e8534ca02609b4916bb4187fb9
languageName: node
linkType: hard
"upath@npm:^1.2.0":
version: 1.2.0
resolution: "upath@npm:1.2.0"
@@ -19355,7 +19436,7 @@ __metadata:
languageName: node
linkType: hard
"xtend@npm:^4.0.0":
"xtend@npm:^4.0.0, xtend@npm:~4.0.1":
version: 4.0.2
resolution: "xtend@npm:4.0.2"
checksum: ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a
@@ -19427,7 +19508,7 @@ __metadata:
languageName: node
linkType: hard
"yargs@npm:^16.2.0":
"yargs@npm:^16.1.0, yargs@npm:^16.2.0":
version: 16.2.0
resolution: "yargs@npm:16.2.0"
dependencies: