Skip to content

Commit 1ee576b

Browse files
committed
Added IPC (for IpcRenderer and IpcMain) and a (very simple) loading screen
1 parent 682ab23 commit 1ee576b

17 files changed

+545
-61
lines changed

electron/IPC/General/IPC.ts

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { BrowserWindow, IpcMain } from "electron";
2+
import { APIChannels, SendChannels } from "./channelsInterface";
3+
4+
export default class IPC {
5+
nameAPI: string = "api";
6+
validSendChannel: SendChannels = {};
7+
validReceiveChannel: string[] = [];
8+
9+
constructor(channels: APIChannels) {
10+
this.nameAPI = channels.nameAPI;
11+
this.validSendChannel = channels.validSendChannel;
12+
this.validReceiveChannel = channels.validReceiveChannel;
13+
}
14+
15+
get channels():APIChannels {
16+
return {
17+
nameAPI: this.nameAPI,
18+
validSendChannel: this.validSendChannel,
19+
validReceiveChannel: this.validReceiveChannel
20+
}
21+
}
22+
23+
initIpcMain(ipcMain:IpcMain, mainWindow: BrowserWindow) {
24+
if (mainWindow) {
25+
Object.keys(this.validSendChannel).forEach(key => {
26+
ipcMain.on(key, async( event, message) => {
27+
this.validSendChannel[key](mainWindow, event, message);
28+
});
29+
});
30+
}
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export interface APIChannels {
2+
nameAPI: string,
3+
validSendChannel: SendChannels,
4+
validReceiveChannel: string[]
5+
}
6+
7+
export interface SendChannels {
8+
[key: string]: Function
9+
}

electron/IPC/General/contextBridge.ts

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { contextBridge, ipcRenderer } from "electron";
2+
import { APIChannels } from "./channelsInterface";
3+
import IPC from "./IPC";
4+
5+
interface APIContextBridge {
6+
send: (channel: string, data: any) => void;
7+
receive: (channel: string, func: (arg0: any) => void) => void;
8+
}
9+
10+
export function generateContextBridge(listIPC: IPC[]) {
11+
12+
let listChannels: APIChannels[] = [];
13+
listIPC.forEach(el => {
14+
listChannels.push(el.channels);
15+
});
16+
17+
let listAPI: {[key: string]: APIContextBridge} = {};
18+
19+
listChannels.forEach(el => {
20+
const api = getContextBridge(el);
21+
const name = el.nameAPI;
22+
listAPI[name] = {...api};
23+
});
24+
25+
contextBridge.exposeInMainWorld("api", {
26+
...listAPI
27+
});
28+
}
29+
30+
function getContextBridge(obj: APIChannels): APIContextBridge {
31+
const { validReceiveChannel } = { ...obj };
32+
const validSendChannel = getArrayOfValidSendChannel(obj);
33+
34+
return {
35+
send: (channel: string, data: any) => {
36+
// whitelist channels
37+
if (validSendChannel.includes(channel)) {
38+
ipcRenderer.send(channel, data);
39+
}
40+
},
41+
receive: (channel: string, func: (arg0: any) => void) => {
42+
if (validReceiveChannel.includes(channel)) {
43+
// Deliberately strip event as it includes `sender`
44+
ipcRenderer.on(channel, (event, ...args: [any]) => {func(...args);});
45+
}
46+
}
47+
}
48+
};
49+
50+
function getArrayOfValidSendChannel(obj: APIChannels): string[] {
51+
const { validSendChannel } = { ...obj };
52+
let result: string[] = Object.keys(validSendChannel);
53+
return result;
54+
}

electron/IPC/systemInfo.ts

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { SendChannels } from "./General/channelsInterface";
2+
import IPC from "./General/IPC";
3+
import { BrowserWindow } from "electron";
4+
5+
const nameAPI = "systemInfo";
6+
7+
// to Main
8+
const validSendChannel: SendChannels = {
9+
"requestSystemInfo": requestSystemInfo
10+
};
11+
12+
// from Main
13+
const validReceiveChannel: string[] = [
14+
"getSystemInfo",
15+
];
16+
17+
const systemInfo = new IPC ({
18+
nameAPI,
19+
validSendChannel,
20+
validReceiveChannel
21+
});
22+
23+
export default systemInfo;
24+
25+
// Enter here the functions for ElectronJS
26+
27+
function requestSystemInfo(mainWindow: BrowserWindow, event: Electron.IpcMainEvent, message: any) {
28+
const versionChrome = process.versions.chrome;
29+
const versionNode = process.versions.node;
30+
const versionElectron = process.versions.electron;
31+
const result = {
32+
chrome: versionChrome,
33+
node: versionNode,
34+
electron: versionElectron
35+
}
36+
mainWindow.webContents.send("getSystemInfo", result);
37+
}
38+

electron/IPC/updaterInfo.ts

+94
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import { BrowserWindow, app } from "electron";
2+
import { AppUpdater, autoUpdater } from "electron-updater";
3+
import { SendChannels } from "./General/channelsInterface";
4+
import IPC from "./General/IPC";
5+
6+
const nameAPI = "updaterInfo";
7+
8+
// to Main
9+
const validSendChannel: SendChannels = {
10+
"requestVersionNumber": requestVersionNumber,
11+
"checkForUpdate": checkForUpdate,
12+
"startDownloadUpdate": startDownloadUpdate,
13+
"quitAndInstall": quitAndInstall,
14+
};
15+
16+
// from Main
17+
const validReceiveChannel: string[] = [
18+
"getVersionNumber",
19+
"checkingForUpdate",
20+
"updateAvailable",
21+
"updateNotAvailable",
22+
"downloadProgress",
23+
"updateDownloaded",
24+
];
25+
26+
class UpdaterInfo extends IPC {
27+
initAutoUpdater(autoUpdater: AppUpdater, mainWindow: BrowserWindow) {
28+
initAutoUpdater(autoUpdater, mainWindow);
29+
}
30+
}
31+
32+
const updaterInfo = new UpdaterInfo ({
33+
nameAPI,
34+
validSendChannel,
35+
validReceiveChannel
36+
});
37+
38+
export default updaterInfo;
39+
40+
// Enter here the functions for ElectronJS
41+
42+
function initAutoUpdater(autoUpdater: AppUpdater, mainWindow: BrowserWindow) {
43+
autoUpdater.on('checking-for-update', () => {
44+
mainWindow.webContents.send("checkingForUpdate", null);
45+
});
46+
47+
autoUpdater.on('error', (err) => { });
48+
49+
autoUpdater.on("update-available", (info: any) => {
50+
mainWindow.webContents.send("updateAvailable", info);
51+
});
52+
53+
autoUpdater.on('download-progress', (info: any) => {
54+
mainWindow.webContents.send("downloadProgress", info);
55+
});
56+
57+
autoUpdater.on("update-downloaded", (info: any) => {
58+
mainWindow.webContents.send("updateDownloaded", info);
59+
});
60+
61+
autoUpdater.on("update-not-available", (info: any) => {
62+
mainWindow.webContents.send("updateNotAvailable", info);
63+
});
64+
}
65+
66+
function requestVersionNumber(mainWindow: BrowserWindow, event: Electron.IpcMainEvent, message: any) {
67+
const version = app.getVersion();
68+
const result = {version};
69+
mainWindow.webContents.send("getVersionNumber", result);
70+
}
71+
72+
function checkForUpdate(mainWindow: BrowserWindow, event: Electron.IpcMainEvent, message: any) {
73+
autoUpdater.autoDownload = false;
74+
autoUpdater.checkForUpdates();
75+
}
76+
77+
function startDownloadUpdate(mainWindow: BrowserWindow, event: Electron.IpcMainEvent, message: any) {
78+
autoUpdater.downloadUpdate();
79+
}
80+
81+
function quitAndInstall(mainWindow: BrowserWindow, event: Electron.IpcMainEvent, message: any) {
82+
autoUpdater.quitAndInstall();
83+
}
84+
85+
86+
87+
88+
89+
90+
91+
92+
93+
94+

electron/configureDev.ts

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import path from "path";
2+
import serve from "electron-serve";
3+
import { exec } from "child_process";
4+
5+
export interface DeveloperOptions {
6+
isInProduction: boolean;
7+
serveSvelteDev: boolean;
8+
buildSvelteDev: boolean;
9+
watchSvelteBuild: boolean;
10+
}
11+
12+
class ConfigureDev {
13+
isInProduction: boolean;
14+
serveSvelteDev: boolean;
15+
buildSvelteDev: boolean;
16+
watchSvelteBuild: boolean;
17+
loadURL: any;
18+
19+
constructor(settings: DeveloperOptions) {
20+
this.isInProduction = settings.isInProduction
21+
this.serveSvelteDev = settings.serveSvelteDev
22+
this.buildSvelteDev = settings.buildSvelteDev
23+
this.watchSvelteBuild = settings.watchSvelteBuild
24+
this.loadURL = null;
25+
26+
this._check_isInProduction();
27+
28+
if (!this.isInProduction && this.serveSvelteDev) this._dev_Svelte();
29+
if (!this.isInProduction && this.buildSvelteDev) this._build_Dist();
30+
if (!this.isInProduction && this.watchSvelteBuild) this._watch_Dist();
31+
if (this.isInProduction || !this.serveSvelteDev) this._serve_Dist();
32+
}
33+
34+
_check_isInProduction() {
35+
if (! this.isInProduction){
36+
this.isInProduction = process.env.NODE_ENV === "production" || !/[\\/]electron/.exec(process.execPath); // !process.execPath.match(/[\\/]electron/);
37+
};
38+
}
39+
_dev_Svelte() {
40+
exec("npm run svelte:dev");
41+
require("electron-reload")(path.join(__dirname, "..", "svelte"));
42+
}
43+
_build_Dist() { exec("npm run svelte:build"); }
44+
_watch_Dist() { require("electron-reload")(path.join(__dirname, "www")); }
45+
_serve_Dist() {
46+
this.loadURL = serve({ directory: "dist/www" });
47+
}
48+
49+
isLocalHost() { return this.serveSvelteDev; }
50+
isElectronServe() { return !this.serveSvelteDev; }
51+
52+
}
53+
54+
export default ConfigureDev;

electron/index.ts

+17-57
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,30 @@
1-
import { app, BrowserWindow } from "electron";
2-
import path from "path";
3-
import serve from "electron-serve";
4-
import { exec } from "child_process";
1+
import { ipcMain } from 'electron';
2+
import { autoUpdater } from "electron-updater";
3+
import Main from "./mainWindow";
4+
5+
import systemInfo from './IPC/systemInfo';
6+
import updaterInfo from './IPC/updaterInfo';
57

68
const developerOptions = {
7-
isInProduction: false, // true if is in production
9+
isInProduction: true, // true if is in production
810
serveSvelteDev: false, // true when you want to watch svelte
9-
buildSvelteDiv: false, // true when you want to build svelte
11+
buildSvelteDev: false, // true when you want to build svelte
1012
watchSvelteBuild: false, // true when you want to watch build svelte
1113
};
1214

13-
if (! developerOptions.isInProduction){
14-
developerOptions.isInProduction = process.env.NODE_ENV === "production" || !/[\\/]electron/.exec(process.execPath); // !process.execPath.match(/[\\/]electron/);
15-
}
16-
let loadURL:any = null;
17-
18-
if (!developerOptions.isInProduction && developerOptions.serveSvelteDev) {
19-
console.log("npm run svelte:dev");
20-
exec("npm run svelte:dev");
21-
console.log("electron-reload svelte dev");
22-
require("electron-reload")(path.join(__dirname, "..", "svelte"));
23-
}
24-
25-
if (!developerOptions.isInProduction && developerOptions.buildSvelteDiv) {
26-
console.log("npm run svelte:build");
27-
exec("npm run svelte:build");
15+
const windowSettings = {
16+
title: "MEMENTO - SvelteKit, Electron, TypeScript",
17+
width: 854,
18+
height: 854
2819
}
2920

30-
if (!developerOptions.isInProduction && developerOptions.watchSvelteBuild) {
31-
console.log("electron-reload www");
32-
require("electron-reload")(path.join(__dirname, "www"));
33-
}
21+
let main = new Main(windowSettings, developerOptions);
3422

23+
main.onEvent.on("window-created", async () => {
24+
systemInfo.initIpcMain(ipcMain, main.window);
25+
updaterInfo.initIpcMain(ipcMain, main.window);
3526

36-
if (developerOptions.isInProduction || !developerOptions.serveSvelteDev) {
37-
console.log("serve dist/www");
38-
loadURL = serve({ directory: "dist/www" });
39-
}
27+
updaterInfo.initAutoUpdater(autoUpdater, main.window);
4028

41-
let mainWindow = null;
42-
43-
const createWindow = async () => {
44-
mainWindow = new BrowserWindow({
45-
width: 854,
46-
height: 480,
47-
webPreferences: {
48-
nodeIntegration: true,
49-
contextIsolation: true,
50-
enableRemoteModule: true,
51-
},
52-
});
53-
54-
if (developerOptions.serveSvelteDev) {
55-
mainWindow.loadURL("http://localhost:3000/");
56-
} else if (loadURL) {
57-
await loadURL(mainWindow);
58-
}
59-
};
60-
61-
app.on("ready", async () => {
62-
app.name = "Svelte Template";
63-
await createWindow();
6429
});
6530

66-
app.on("window-all-closed", () => {
67-
if (process.platform !== "darwin") {
68-
app.quit();
69-
}
70-
});

0 commit comments

Comments
 (0)