From c00dc4a884abe919d4eded2e0939d5dfcecc2847 Mon Sep 17 00:00:00 2001 From: WardPearce Date: Sun, 1 Sep 2024 14:08:21 +1200 Subject: [PATCH] Include electron build dir --- materialious/electron/.gitignore | 1 - .../electron/build/capacitor.config.js | 14 ++ materialious/electron/build/logo.png | Bin 0 -> 3059 bytes materialious/electron/build/src/index.js | 61 ++++++ materialious/electron/build/src/preload.js | 4 + .../electron/build/src/rt/electron-plugins.js | 2 + .../electron/build/src/rt/electron-rt.js | 68 ++++++ materialious/electron/build/src/setup.js | 202 ++++++++++++++++++ 8 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 materialious/electron/build/capacitor.config.js create mode 100644 materialious/electron/build/logo.png create mode 100644 materialious/electron/build/src/index.js create mode 100644 materialious/electron/build/src/preload.js create mode 100644 materialious/electron/build/src/rt/electron-plugins.js create mode 100644 materialious/electron/build/src/rt/electron-rt.js create mode 100644 materialious/electron/build/src/setup.js diff --git a/materialious/electron/.gitignore b/materialious/electron/.gitignore index c340937a..67a91a7c 100644 --- a/materialious/electron/.gitignore +++ b/materialious/electron/.gitignore @@ -3,6 +3,5 @@ # And the CLI then renames it app node_modules -build dist logs diff --git a/materialious/electron/build/capacitor.config.js b/materialious/electron/build/capacitor.config.js new file mode 100644 index 00000000..d5819777 --- /dev/null +++ b/materialious/electron/build/capacitor.config.js @@ -0,0 +1,14 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const config = { + appId: 'us.materialio.app', + appName: 'Materialious', + webDir: 'build', + bundledWebRuntime: false, + plugins: { + CapacitorHttp: { + enabled: true + } + }, +}; +exports.default = config; diff --git a/materialious/electron/build/logo.png b/materialious/electron/build/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6f3a662cd24ca8759b0baf0040521a66b0bd185c GIT binary patch literal 3059 zcmbW3_fu2b7RS#yo@fde4iI9vj}lN(Ohnoh5QTsRlzJsJL5YB3dy1e4DsVJ_!HUwQ z7!?o#61qrBgecO2=tYrcsEPs#h!|R4?wh%PzP zmVZTDzR#DW)eZ77%Zeu_XbyrW(?sXUj+VtorkEo(ROP_05yzB^^4ujQWqfq$9lZFU zjDPvjJGjQH>l1&=X$QfN(h@#Ywp?7mL;FPVVqwAN%S$rP&&4^f&%ph1@LZ{OPKQ%O z-VhGy^x-O_aT~_W1KsBD(+|wUxo_+JiUQQXw?EY3n9(lj7Hq+9-q9JdFymyYdjdLQ zzKh2ag))Ae7pq=89Eo8ZKQVinQxkvZ2!GsR;9bS%HYe`%ueT)tCSv*cS1wZPH8>TT zZBezTQ8Rnyz#wegX4Agg>{ogpoJ+3@9iNh09HD&MPMV{H z&-|Rl(wTsEH5T&uACwzOP27iD@v$LW4s_^N?2 zv5F;hb3|K z#r+;S{TxRiZ=*>kOM3HDGxbzYytN|<8MN;lwIAM>c$HA>lit)MJYTkA)YQdc>2+MJJ$E`Mq`q>d zhn9pfUrffl@6|zx0d1{CG;v&P{Iie@C^%i$T4yx#%7;54F_koNM(j_sqbdPh3m?)p zlb;heT#9(L6k#ftXC&HLp1)m7m^xTP>w4WEmxPJoZfi@5?O9f)=Qo$8tfPJR!gjfj#CW-$l0Qd>-42&OW?6Sz zY;hl9`KRVieio3=N9=Jm>vPEOd9WT$iv1&vyX`}VVvZPhXjR4@*G#oBERu{-;|7I4 ze8jv}+MOP&>nLmUEqIf%Wo@#A@qv#lFh*u3hI(7B{HCrrRKjVBp(^R#%}H}h+e&CK=aZ=M6j#aaOdR=! zQT|19tqA^_5Uq-vrL_oaxLZ6ID1qbwhgjw^+YG_{Oy$K;U1Mh7~Vh= zG~5J}adFv5uo7hoXJ82i5q;etIQ*<_7SMU<1LVI!*b3WbvSi}Jeum>Ht@aIpvh-w6 z+J;-VI-$b#!bO-OtsJhx$5kM~M9PSRZW2W6Phpe*ID&x_s6_J3gtDkYZ;Wk-bt?n) zk50)4n(zkhpx!}Cj}jo(k^KQ5I6MD*AI8>&Ey2f)c4 wM|wcC~JeT0Au?nJB)xx zqf(v$Qv`!x@ZraVK|T5p>f3P)+nX&RAYlRNwZNT7yB#Q?7~#Dy0XRb_hCwV()B7~I z@!b9jz<^MU1+_;xa-cTJR}0!n74C!CNi3PTvR`14eB`tI|fw4a9XdJQ}<&gqBClnilS1h-#bwuer%>*i4 z9F1-R>q)+sp{N5`TOwun>#B8lqzti>A+FdKw9Z7X6Di``c6b#^@1q^o7;K2k-U6UA zVgQ0S3WG3M2Kxmb$wdexN-M6I30@)kHbJni@Gb^BHE;C+f=#tZo9O6!g=P|ku9dV2 zl|I^0kNMtiH3t16`A+>U66jY3!n+JaM0?Z$5hmI1=m4Mk_A3Q-(mF^0FNM5(Zw=i3 z_{Kv4+!o;-v>ndnDzuOU5`NtlsFF0O0(wWLTw?)+(V@G5_xgt^=n1WZODmmku>ui6 zF~Y-($vHGQkSffF7^ir8zS`*9mw|S`Q>zrLcpuvws!T62R21YPPcebNN5ukq>xBnE zwfcmcfV^tIia^W$1D46ZF`+21u$N$&k=DIy(4}%e3&j1|S=mQ4ypM909RruiSwl zX$A!iJD-=mIpX**t;LC(@)f%B7=uZ{uRYNbt1Mhm!hiIvRVmY9NthP^w`eEr1r z0@s)Y?DvrKo*W=Ky2}QA`nW&|Q`s808Pq$Uybk|er(p@s{i0k9)iecKg8fl>_8>`h zQdh7mrJ2at_pfEh`@f;I?mCE8&&UO?_lWwT^s9LcP-NKagO=*6Q0cDf5<(y;#}|{! z { + // Wait for electron app to be ready. + await electron_2.app.whenReady(); + // Security - Set Content-Security-Policy based on whether or not we are in dev mode. + (0, setup_1.setupContentSecurityPolicy)(myCapacitorApp.getCustomURLScheme()); + // Initialize our app, build windows, and load content. + await myCapacitorApp.init(); + // Check for updates if we are in a packaged app. + electron_updater_1.autoUpdater.checkForUpdatesAndNotify(); +})(); +// Handle when all of our windows are close (platforms have their own expectations). +electron_2.app.on('window-all-closed', function () { + // On OS X it is common for applications and their menu bar + // to stay active until the user quits explicitly with Cmd + Q + if (process.platform !== 'darwin') { + electron_2.app.quit(); + } +}); +// When the dock icon is clicked. +electron_2.app.on('activate', async function () { + // 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 (myCapacitorApp.getMainWindow().isDestroyed()) { + await myCapacitorApp.init(); + } +}); +// Place all ipc or other electron api calls and custom functionality under this line diff --git a/materialious/electron/build/src/preload.js b/materialious/electron/build/src/preload.js new file mode 100644 index 00000000..c817d3b7 --- /dev/null +++ b/materialious/electron/build/src/preload.js @@ -0,0 +1,4 @@ +require('./rt/electron-rt'); +////////////////////////////// +// User Defined Preload scripts below +console.log('User Preload!'); diff --git a/materialious/electron/build/src/rt/electron-plugins.js b/materialious/electron/build/src/rt/electron-plugins.js new file mode 100644 index 00000000..0141d0c9 --- /dev/null +++ b/materialious/electron/build/src/rt/electron-plugins.js @@ -0,0 +1,2 @@ +/* eslint-disable @typescript-eslint/no-var-requires */ +module.exports = {}; diff --git a/materialious/electron/build/src/rt/electron-rt.js b/materialious/electron/build/src/rt/electron-rt.js new file mode 100644 index 00000000..6260d86a --- /dev/null +++ b/materialious/electron/build/src/rt/electron-rt.js @@ -0,0 +1,68 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +const crypto_1 = require("crypto"); +const electron_1 = require("electron"); +const events_1 = require("events"); +//////////////////////////////////////////////////////// +// eslint-disable-next-line @typescript-eslint/no-var-requires +const plugins = require('./electron-plugins'); +const randomId = (length = 5) => (0, crypto_1.randomBytes)(length).toString('hex'); +const contextApi = {}; +Object.keys(plugins).forEach((pluginKey) => { + Object.keys(plugins[pluginKey]) + .filter((className) => className !== 'default') + .forEach((classKey) => { + const functionList = Object.getOwnPropertyNames(plugins[pluginKey][classKey].prototype).filter((v) => v !== 'constructor'); + if (!contextApi[classKey]) { + contextApi[classKey] = {}; + } + functionList.forEach((functionName) => { + if (!contextApi[classKey][functionName]) { + contextApi[classKey][functionName] = (...args) => electron_1.ipcRenderer.invoke(`${classKey}-${functionName}`, ...args); + } + }); + // Events + if (plugins[pluginKey][classKey].prototype instanceof events_1.EventEmitter) { + const listeners = {}; + const listenersOfTypeExist = (type) => !!Object.values(listeners).find((listenerObj) => listenerObj.type === type); + Object.assign(contextApi[classKey], { + addListener(type, callback) { + const id = randomId(); + // Deduplicate events + if (!listenersOfTypeExist(type)) { + electron_1.ipcRenderer.send(`event-add-${classKey}`, type); + } + const eventHandler = (_, ...args) => callback(...args); + electron_1.ipcRenderer.addListener(`event-${classKey}-${type}`, eventHandler); + listeners[id] = { type, listener: eventHandler }; + return id; + }, + removeListener(id) { + if (!listeners[id]) { + throw new Error('Invalid id'); + } + const { type, listener } = listeners[id]; + electron_1.ipcRenderer.removeListener(`event-${classKey}-${type}`, listener); + delete listeners[id]; + if (!listenersOfTypeExist(type)) { + electron_1.ipcRenderer.send(`event-remove-${classKey}-${type}`); + } + }, + removeAllListeners(type) { + Object.entries(listeners).forEach(([id, listenerObj]) => { + if (!type || listenerObj.type === type) { + electron_1.ipcRenderer.removeListener(`event-${classKey}-${listenerObj.type}`, listenerObj.listener); + electron_1.ipcRenderer.send(`event-remove-${classKey}-${listenerObj.type}`); + delete listeners[id]; + } + }); + }, + }); + } + }); +}); +electron_1.contextBridge.exposeInMainWorld('CapacitorCustomPlatform', { + name: 'electron', + plugins: contextApi, +}); +//////////////////////////////////////////////////////// diff --git a/materialious/electron/build/src/setup.js b/materialious/electron/build/src/setup.js new file mode 100644 index 00000000..859ca4d7 --- /dev/null +++ b/materialious/electron/build/src/setup.js @@ -0,0 +1,202 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.ElectronCapacitorApp = void 0; +exports.setupReloadWatcher = setupReloadWatcher; +exports.setupContentSecurityPolicy = setupContentSecurityPolicy; +const tslib_1 = require("tslib"); +const electron_1 = require("@capacitor-community/electron"); +const chokidar_1 = tslib_1.__importDefault(require("chokidar")); +const electron_2 = require("electron"); +const electron_is_dev_1 = tslib_1.__importDefault(require("electron-is-dev")); +const electron_serve_1 = tslib_1.__importDefault(require("electron-serve")); +const electron_window_state_1 = tslib_1.__importDefault(require("electron-window-state")); +const path_1 = require("path"); +// Define components for a watcher to detect when the webapp is changed so we can reload in Dev mode. +const reloadWatcher = { + debouncer: null, + ready: false, + watcher: null, +}; +function setupReloadWatcher(electronCapacitorApp) { + reloadWatcher.watcher = chokidar_1.default + .watch((0, path_1.join)(electron_2.app.getAppPath(), 'app'), { + ignored: /[/\\]\./, + persistent: true, + }) + .on('ready', () => { + reloadWatcher.ready = true; + }) + .on('all', (_event, _path) => { + if (reloadWatcher.ready) { + clearTimeout(reloadWatcher.debouncer); + reloadWatcher.debouncer = setTimeout(async () => { + electronCapacitorApp.getMainWindow().webContents.reload(); + reloadWatcher.ready = false; + clearTimeout(reloadWatcher.debouncer); + reloadWatcher.debouncer = null; + reloadWatcher.watcher = null; + setupReloadWatcher(electronCapacitorApp); + }, 1500); + } + }); +} +// Define our class to manage our app. +class ElectronCapacitorApp { + constructor(capacitorFileConfig, trayMenuTemplate, appMenuBarMenuTemplate) { + var _a, _b; + this.MainWindow = null; + this.SplashScreen = null; + this.TrayIcon = null; + this.TrayMenuTemplate = [ + new electron_2.MenuItem({ label: 'Quit App', role: 'quit' }), + ]; + this.AppMenuBarMenuTemplate = [ + { role: process.platform === 'darwin' ? 'appMenu' : 'fileMenu' }, + { role: 'viewMenu' }, + ]; + this.CapacitorFileConfig = capacitorFileConfig; + this.customScheme = (_b = (_a = this.CapacitorFileConfig.electron) === null || _a === void 0 ? void 0 : _a.customUrlScheme) !== null && _b !== void 0 ? _b : 'capacitor-electron'; + if (trayMenuTemplate) { + this.TrayMenuTemplate = trayMenuTemplate; + } + if (appMenuBarMenuTemplate) { + this.AppMenuBarMenuTemplate = appMenuBarMenuTemplate; + } + // Setup our web app loader, this lets us load apps like react, vue, and angular without changing their build chains. + this.loadWebApp = (0, electron_serve_1.default)({ + directory: (0, path_1.join)(electron_2.app.getAppPath(), 'app'), + scheme: this.customScheme, + }); + } + // Helper function to load in the app. + async loadMainWindow(thisRef) { + await thisRef.loadWebApp(thisRef.MainWindow); + } + // Expose the mainWindow ref for use outside of the class. + getMainWindow() { + return this.MainWindow; + } + getCustomURLScheme() { + return this.customScheme; + } + async init() { + var _a, _b, _c, _d; + const icon = electron_2.nativeImage.createFromPath((0, path_1.join)(electron_2.app.getAppPath(), 'assets', process.platform === 'win32' ? 'appIcon.ico' : 'appIcon.png')); + this.mainWindowState = (0, electron_window_state_1.default)({ + defaultWidth: 1000, + defaultHeight: 800, + }); + // Setup preload script path and construct our main window. + const preloadPath = (0, path_1.join)(electron_2.app.getAppPath(), 'build', 'src', 'preload.js'); + this.MainWindow = new electron_2.BrowserWindow({ + icon, + show: false, + x: this.mainWindowState.x, + y: this.mainWindowState.y, + width: this.mainWindowState.width, + height: this.mainWindowState.height, + webPreferences: { + nodeIntegration: true, + contextIsolation: true, + webSecurity: false, + // Use preload to inject the electron varriant overrides for capacitor plugins. + // preload: join(app.getAppPath(), "node_modules", "@capacitor-community", "electron", "dist", "runtime", "electron-rt.js"), + preload: preloadPath, + }, + }); + this.mainWindowState.manage(this.MainWindow); + if (this.CapacitorFileConfig.backgroundColor) { + this.MainWindow.setBackgroundColor(this.CapacitorFileConfig.electron.backgroundColor); + } + // If we close the main window with the splashscreen enabled we need to destory the ref. + this.MainWindow.on('closed', () => { + var _a; + if (((_a = this.SplashScreen) === null || _a === void 0 ? void 0 : _a.getSplashWindow()) && !this.SplashScreen.getSplashWindow().isDestroyed()) { + this.SplashScreen.getSplashWindow().close(); + } + }); + // When the tray icon is enabled, setup the options. + if ((_a = this.CapacitorFileConfig.electron) === null || _a === void 0 ? void 0 : _a.trayIconAndMenuEnabled) { + this.TrayIcon = new electron_2.Tray(icon); + this.TrayIcon.on('double-click', () => { + if (this.MainWindow) { + if (this.MainWindow.isVisible()) { + this.MainWindow.hide(); + } + else { + this.MainWindow.show(); + this.MainWindow.focus(); + } + } + }); + this.TrayIcon.on('click', () => { + if (this.MainWindow) { + if (this.MainWindow.isVisible()) { + this.MainWindow.hide(); + } + else { + this.MainWindow.show(); + this.MainWindow.focus(); + } + } + }); + this.TrayIcon.setToolTip(electron_2.app.getName()); + this.TrayIcon.setContextMenu(electron_2.Menu.buildFromTemplate(this.TrayMenuTemplate)); + } + // Setup the main manu bar at the top of our window. + electron_2.Menu.setApplicationMenu(electron_2.Menu.buildFromTemplate(this.AppMenuBarMenuTemplate)); + // If the splashscreen is enabled, show it first while the main window loads then switch it out for the main window, or just load the main window from the start. + if ((_b = this.CapacitorFileConfig.electron) === null || _b === void 0 ? void 0 : _b.splashScreenEnabled) { + this.SplashScreen = new electron_1.CapacitorSplashScreen({ + imageFilePath: (0, path_1.join)(electron_2.app.getAppPath(), 'assets', (_d = (_c = this.CapacitorFileConfig.electron) === null || _c === void 0 ? void 0 : _c.splashScreenImageName) !== null && _d !== void 0 ? _d : 'splash.png'), + windowWidth: 400, + windowHeight: 400, + }); + this.SplashScreen.init(this.loadMainWindow, this); + } + else { + this.loadMainWindow(this); + } + // Security + this.MainWindow.webContents.setWindowOpenHandler((details) => { + if (!details.url.includes(this.customScheme)) { + return { action: 'deny' }; + } + else { + return { action: 'allow' }; + } + }); + this.MainWindow.webContents.on('will-navigate', (event, _newURL) => { + if (!this.MainWindow.webContents.getURL().includes(this.customScheme)) { + event.preventDefault(); + } + }); + // Link electron plugins into the system. + (0, electron_1.setupCapacitorElectronPlugins)(); + // When the web app is loaded we hide the splashscreen if needed and show the mainwindow. + this.MainWindow.webContents.on('dom-ready', () => { + var _a, _b; + if ((_a = this.CapacitorFileConfig.electron) === null || _a === void 0 ? void 0 : _a.splashScreenEnabled) { + this.SplashScreen.getSplashWindow().hide(); + } + if (!((_b = this.CapacitorFileConfig.electron) === null || _b === void 0 ? void 0 : _b.hideMainWindowOnLaunch)) { + this.MainWindow.show(); + } + setTimeout(() => { + if (electron_is_dev_1.default) { + this.MainWindow.webContents.openDevTools(); + } + electron_1.CapElectronEventEmitter.emit('CAPELECTRON_DeeplinkListenerInitialized', ''); + }, 400); + }); + } +} +exports.ElectronCapacitorApp = ElectronCapacitorApp; +// Set a CSP up for our application based on the custom scheme +function setupContentSecurityPolicy(customScheme) { + electron_2.session.defaultSession.webRequest.onHeadersReceived((details, callback) => { + callback({ + responseHeaders: Object.assign({}, details.responseHeaders), + }); + }); +}