Skip to content

Commit

Permalink
feat: wireup node installation flow [#30]
Browse files Browse the repository at this point in the history
  • Loading branch information
Yukaii committed Aug 13, 2023
1 parent 8a41890 commit 7d974bd
Show file tree
Hide file tree
Showing 17 changed files with 220 additions and 8 deletions.
8 changes: 8 additions & 0 deletions apps/electron-client/forge.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ const config: ForgeConfig = {
js: "./src/preload.ts",
},
},
{
html: "./src/nodeInstaller.html",
js: "./src/nodeInstaller/renderer.ts",
name: "node_installer",
preload: {
js: "./src/nodeInstaller/preload.ts",
},
}
],
},
}),
Expand Down
1 change: 1 addition & 0 deletions apps/electron-client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
},
"dependencies": {
"@blastlauncher/runtime": "workspace:*",
"@blastlauncher/utils": "workspace:*",
"electron-squirrel-startup": "^1.0.0"
},
"devDependencies": {
Expand Down
5 changes: 5 additions & 0 deletions apps/electron-client/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import os from 'os'
import path from 'path'

export const USER_DIR = path.join(os.homedir(), '.blast')
export const NODE_INSTALL_PATH = path.join(USER_DIR, 'node')
2 changes: 1 addition & 1 deletion apps/electron-client/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World!</title>
<title>Blast</title>
</head>
<body>
<div id="app"></div>
Expand Down
17 changes: 12 additions & 5 deletions apps/electron-client/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,26 @@ import { app, BrowserWindow, globalShortcut } from "electron";
import installExtension, { REACT_DEVELOPER_TOOLS } from "electron-devtools-installer";

import { setMenu } from "./menu";
import { registerIPCMainEvents } from "./nodeInstaller/events";
import { hasVersionInstalled } from "./nrm";
import { startRuntime } from "./runtime";
import { createTray } from "./tray";
import { createWindow } from "./window";
import { createApplicationWindow, createNodeInstallerWindow } from "./window";

// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require("electron-squirrel-startup")) {
app.quit();
}

const onReady = (): void => {
startRuntime();
setMenu();
createWindow();
if (!hasVersionInstalled()) {
createNodeInstallerWindow();
registerIPCMainEvents();
} else {
startRuntime();
setMenu();
createApplicationWindow();
}
};

// This method will be called when Electron has finished
Expand All @@ -41,7 +48,7 @@ app.on("activate", () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
createApplicationWindow();
}
});

Expand Down
10 changes: 10 additions & 0 deletions apps/electron-client/src/nodeInstaller.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Blast Node Installer</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
47 changes: 47 additions & 0 deletions apps/electron-client/src/nodeInstaller/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useCallback, useState } from "react";

export default function App() {
const [error, setError] = useState<string | null>(null);
const [isInstalling, setIsInstalling] = useState(false);

const onInstall = useCallback(() => {
if (isInstalling) {
return;
}

setError(null);
setIsInstalling(true);

window.electron
.startNodeInstallation()
.then((success) => {
if (!success) {
setError("Installation failed");
}

window.electron.exitAndStart();
})
.catch((error) => {
setError(error.message);
})
.finally(() => {
setIsInstalling(false);
});
}, []);

return (
<div className="flex justify-center items-center text-white h-full flex-col gap-2">
{!isInstalling && (
<>
<p>You have to install the nodejs runtime to use this app. Click the button below to proceed.</p>

<button className="py-2 px-4 font-bold text-white bg-blue-500 rounded hover:bg-blue-700" onClick={onInstall}>
Install NodeJS
</button>
</>
)}

{isInstalling && <p>Installing...</p>}
</div>
);
}
33 changes: 33 additions & 0 deletions apps/electron-client/src/nodeInstaller/events.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { ipcMain } from "electron";

import { setMenu } from "../menu";
import { installNode, nrm, hasVersionInstalled } from "../nrm";
import { startRuntime } from "../runtime";
import { closeNodeInstallerWindow, createApplicationWindow } from "../window";

import { EventTypes } from "./types";

export function registerIPCMainEvents() {
ipcMain.handle(EventTypes.INSTALL_NODE, async () => {
if (hasVersionInstalled()) {
return true;
}

await installNode();

try {
nrm.nodePath;
} catch (error) {
return false;
}

return true;
});

ipcMain.handle(EventTypes.EXIT_AND_START, async () => {
closeNodeInstallerWindow();
await startRuntime();
setMenu();
createApplicationWindow();
});
}
6 changes: 6 additions & 0 deletions apps/electron-client/src/nodeInstaller/extendWindow.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
interface Window {
electron: {
startNodeInstallation: () => Promise<boolean>;
exitAndStart: () => Promise<void>;
};
}
9 changes: 9 additions & 0 deletions apps/electron-client/src/nodeInstaller/preload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { contextBridge, ipcRenderer } from 'electron'

import { EventTypes } from './types'

contextBridge.exposeInMainWorld('electron', {
startNodeInstallation: () => ipcRenderer.invoke(EventTypes.INSTALL_NODE),
exitAndStart: () => ipcRenderer.invoke(EventTypes.EXIT_AND_START)
})

18 changes: 18 additions & 0 deletions apps/electron-client/src/nodeInstaller/renderer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from "react";
import { createRoot } from "react-dom/client";
import './styles.scss'

import App from "./App";

function start() {
const container = document.getElementById("app");
if (!container) {
return;
}

const root = createRoot(container);

root.render(React.createElement(App));
}

start();
9 changes: 9 additions & 0 deletions apps/electron-client/src/nodeInstaller/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

html,
body,
#app {
height: 100%;
}
4 changes: 4 additions & 0 deletions apps/electron-client/src/nodeInstaller/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const enum EventTypes {
INSTALL_NODE = 'INSTALL_NODE',
EXIT_AND_START = 'EXIT_AND_START',
}
18 changes: 18 additions & 0 deletions apps/electron-client/src/nrm.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { NRM } from '@blastlauncher/utils'

import { NODE_INSTALL_PATH } from './constants'

const NODE_VERSION = "v18.17.1"

export const nrm = new NRM({
installPath: NODE_INSTALL_PATH,
})

export function hasVersionInstalled () {
return nrm.hasVersion(NODE_VERSION)
}

export function installNode () {
return nrm.download(NODE_VERSION)
}

2 changes: 1 addition & 1 deletion apps/electron-client/src/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

let runtimeProcess: any;

export const startRuntime = (): void => {
export const startRuntime = async () => {
// const modulePath = require.resolve('@blastlauncher/runtime/dist/run.js')
// runtimeProcess = utilityProcess.fork(modulePath)
}
Expand Down
37 changes: 36 additions & 1 deletion apps/electron-client/src/window.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { app, BrowserWindow, globalShortcut } from "electron";
declare const MAIN_WINDOW_WEBPACK_ENTRY: string;
declare const MAIN_WINDOW_PRELOAD_WEBPACK_ENTRY: string;

declare const NODE_INSTALLER_WEBPACK_ENTRY: string;
declare const NODE_INSTALLER_PRELOAD_WEBPACK_ENTRY: string;

let mainWindow: BrowserWindow | null = null;
let nodeInstallerWindow: BrowserWindow | null = null;

export const toggleMainWindowVisibility = (): void => {
if (!mainWindow) {
Expand All @@ -21,7 +25,7 @@ export const toggleMainWindowVisibility = (): void => {
}
};

export const createWindow = (): void => {
export const createApplicationWindow = (): void => {
// Create the browser window.
mainWindow = new BrowserWindow({
height: 475,
Expand Down Expand Up @@ -64,3 +68,34 @@ export const showMainWindow = (): void => {
}
}

export const createNodeInstallerWindow = (): void => {
nodeInstallerWindow = new BrowserWindow({
height: 475,
width: 750,
darkTheme: true,
frame: false,
vibrancy: "ultra-dark",
transparent: true,
resizable: false,
backgroundColor: "#00000000",
visualEffectState: "followWindow",
thickFrame: false,
minimizable: false,
webPreferences: {
preload: NODE_INSTALLER_PRELOAD_WEBPACK_ENTRY,
},
});

nodeInstallerWindow.loadURL(NODE_INSTALLER_WEBPACK_ENTRY);

if (process.env.NODE_ENV === "development") {
nodeInstallerWindow.webContents.openDevTools();
}
}

export const closeNodeInstallerWindow = (): void => {
if (nodeInstallerWindow) {
nodeInstallerWindow.close();
}
}

2 changes: 2 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 7d974bd

Please sign in to comment.