diff --git a/.prettierrc b/.prettierrc index 1abbcd5..917ad26 100644 --- a/.prettierrc +++ b/.prettierrc @@ -6,5 +6,6 @@ "bracketSpacing": true, "jsxBracketSameLine": true, "arrowParens": "always", - "semi": false + "semi": false, + "endOfLine": "lf" } \ No newline at end of file diff --git a/app/electron/main.js b/app/electron/main.js index 5e09c9b..011a3ca 100644 --- a/app/electron/main.js +++ b/app/electron/main.js @@ -4,33 +4,33 @@ const { BrowserWindow, session, ipcMain, - Menu -} = require("electron"); + Menu, +} = require('electron') const { default: installExtension, REDUX_DEVTOOLS, - REACT_DEVELOPER_TOOLS -} = require("electron-devtools-installer"); -const SecureElectronLicenseKeys = require("secure-electron-license-keys"); -const Protocol = require("./protocol"); -const MenuBuilder = require("./menu"); -const i18nextBackend = require("i18next-electron-fs-backend"); -const i18nextMainBackend = require("../localization/i18n.mainconfig"); -const Store = require("secure-electron-store").default; -const ContextMenu = require("secure-electron-context-menu").default; -const path = require("path"); -const fs = require("fs"); -const crypto = require("crypto"); -const isDev = process.env.NODE_ENV === "development"; -const port = 40992; // Hardcoded; needs to match webpack.development.js and package.json -const selfHost = `http://localhost:${port}`; + REACT_DEVELOPER_TOOLS, +} = require('electron-devtools-installer') +const SecureElectronLicenseKeys = require('secure-electron-license-keys') +const Protocol = require('./protocol') +const MenuBuilder = require('./menu') +const i18nextBackend = require('i18next-electron-fs-backend') +const i18nextMainBackend = require('../localization/i18n.mainconfig') +const Store = require('secure-electron-store').default +const ContextMenu = require('secure-electron-context-menu').default +const path = require('path') +const fs = require('fs') +const crypto = require('crypto') +const isDev = process.env.NODE_ENV === 'development' +const port = 40992 // Hardcoded; needs to match webpack.development.js and package.json +const selfHost = `http://localhost:${port}` const { autoDetect, ILaunchpad, RgbColor } = require('launchpad.js') const { colorFromRGB } = require('launchpad.js/dist/colorHelpers') const { DMX, EnttecUSBDMXProDriver, UniverseData } = require('dmx-ts') -let win; -let menuBuilder; +let win +let menuBuilder let lp @@ -39,17 +39,20 @@ let universe async function createWindow() { if (!isDev) { - protocol.registerBufferProtocol(Protocol.scheme, Protocol.requestHandler); /* eng-disable PROTOCOL_HANDLER_JS_CHECK */ + protocol.registerBufferProtocol( + Protocol.scheme, + Protocol.requestHandler + ) /* eng-disable PROTOCOL_HANDLER_JS_CHECK */ } const store = new Store({ - path: app.getPath("userData") - }); + path: app.getPath('userData'), + }) win = new BrowserWindow({ width: 1200, height: 800, - title: "Application is currently initializing...", + title: 'Application is currently initializing...', webPreferences: { devTools: isDev, nodeIntegration: false, @@ -57,95 +60,107 @@ async function createWindow() { nodeIntegrationInSubFrames: false, contextIsolation: true, enableRemoteModule: false, - additionalArguments: [`--storePath=${store.sanitizePath(app.getPath("userData"))}`], - preload: path.join(__dirname, "preload.js"), + additionalArguments: [ + `--storePath=${store.sanitizePath(app.getPath('userData'))}`, + ], + preload: path.join(__dirname, 'preload.js'), /* eng-disable PRELOAD_JS_CHECK */ - disableBlinkFeatures: "Auxclick" - } - }); + disableBlinkFeatures: 'Auxclick', + }, + }) // Sets up main.js bindings for our i18next backend - i18nextBackend.mainBindings(ipcMain, win, fs); + i18nextBackend.mainBindings(ipcMain, win, fs) // Sets up main.js bindings for our electron store; // callback is optional and allows you to use store in main process const callback = function (success, initialStore) { - console.log(`${!success ? "Un-s" : "S"}uccessfully retrieved store in main process.`); - console.log(initialStore); // {"key1": "value1", ... } - }; + console.log( + `${!success ? 'Un-s' : 'S'}uccessfully retrieved store in main process.` + ) + console.log(initialStore) // {"key1": "value1", ... } + } - store.mainBindings(ipcMain, win, fs, callback); + store.mainBindings(ipcMain, win, fs, callback) // Sets up bindings for our custom context menu ContextMenu.mainBindings(ipcMain, win, Menu, isDev, { - "loudAlertTemplate": [{ - id: "loudAlert", - label: "AN ALERT!" - }], - "softAlertTemplate": [{ - id: "softAlert", - label: "Soft alert" - }] - }); + loudAlertTemplate: [ + { + id: 'loudAlert', + label: 'AN ALERT!', + }, + ], + softAlertTemplate: [ + { + id: 'softAlert', + label: 'Soft alert', + }, + ], + }) // Setup bindings for offline license verification SecureElectronLicenseKeys.mainBindings(ipcMain, win, fs, crypto, { root: process.cwd(), - version: app.getVersion() - }); + version: app.getVersion(), + }) // Load app if (isDev) { - win.loadURL(selfHost); + win.loadURL(selfHost) } else { - win.loadURL(`${Protocol.scheme}://rse/index.html`); + win.loadURL(`${Protocol.scheme}://rse/index.html`) } - win.webContents.on("did-finish-load", () => { - win.setTitle(`Getting started with secure-electron-template (v${app.getVersion()})`); - }); + win.webContents.on('did-finish-load', () => { + win.setTitle( + `Getting started with secure-electron-template (v${app.getVersion()})` + ) + }) // Only do these things when in development if (isDev) { - // Errors are thrown if the dev tools are opened // before the DOM is ready - win.webContents.once("dom-ready", async () => { + win.webContents.once('dom-ready', async () => { await installExtension([REDUX_DEVTOOLS, REACT_DEVELOPER_TOOLS]) .then((name) => console.log(`Added Extension: ${name}`)) - .catch((err) => console.log("An error occurred: ", err)) + .catch((err) => console.log('An error occurred: ', err)) .finally(() => { - require("electron-debug")(); // https://github.com/sindresorhus/electron-debug - win.webContents.openDevTools(); - }); - }); + require('electron-debug')() // https://github.com/sindresorhus/electron-debug + win.webContents.openDevTools() + }) + }) } // Emitted when the window is closed. - win.on("closed", () => { + win.on('closed', () => { // Dereference the window object, usually you would store windows // in an array if your app supports multi windows, this is the time // when you should delete the corresponding element. - win = null; - }); + win = null + }) // https://electronjs.org/docs/tutorial/security#4-handle-session-permission-requests-from-remote-content - const ses = session; - const partition = "default"; - ses.fromPartition(partition) /* eng-disable PERMISSION_REQUEST_HANDLER_JS_CHECK */ + const ses = session + const partition = 'default' + ses + .fromPartition( + partition + ) /* eng-disable PERMISSION_REQUEST_HANDLER_JS_CHECK */ .setPermissionRequestHandler((webContents, permission, permCallback) => { - const allowedPermissions = []; // Full list here: https://developer.chrome.com/extensions/declare_permissions#manifest + const allowedPermissions = [] // Full list here: https://developer.chrome.com/extensions/declare_permissions#manifest if (allowedPermissions.includes(permission)) { - permCallback(true); // Approve permission request + permCallback(true) // Approve permission request } else { console.error( `The application tried to request permission for '${permission}'. This permission was not whitelisted and has been blocked.` - ); + ) - permCallback(false); // Deny + permCallback(false) // Deny } - }); + }) // https://electronjs.org/docs/tutorial/security#1-only-load-secure-content; // The below code can only run when a scheme and host are defined, I thought @@ -158,115 +173,118 @@ async function createWindow() { // } // }); - menuBuilder = MenuBuilder(win, app.name); + menuBuilder = MenuBuilder(win, app.name) // Set up necessary bindings to update the menu items // based on the current language selected - i18nextMainBackend.on("initialized", (loaded) => { - i18nextMainBackend.changeLanguage("en"); - i18nextMainBackend.off("initialized"); // Remove listener to this event as it's not needed anymore - }); + i18nextMainBackend.on('initialized', (loaded) => { + i18nextMainBackend.changeLanguage('en') + i18nextMainBackend.off('initialized') // Remove listener to this event as it's not needed anymore + }) // When the i18n framework starts up, this event is called // (presumably when the default language is initialized) - // BEFORE the "initialized" event is fired - this causes an + // BEFORE the "initialized" event is fired - this causes an // error in the logs. To prevent said error, we only call the // below code until AFTER the i18n framework has finished its // "initialized" event. - i18nextMainBackend.on("languageChanged", (lng) => { + i18nextMainBackend.on('languageChanged', (lng) => { if (i18nextMainBackend.isInitialized) { - menuBuilder.buildMenu(i18nextMainBackend); + menuBuilder.buildMenu(i18nextMainBackend) } - }); + }) } // Needs to be called before app is ready; // gives our scheme access to load relative files, // as well as local storage, cookies, etc. // https://electronjs.org/docs/api/protocol#protocolregisterschemesasprivilegedcustomschemes -protocol.registerSchemesAsPrivileged([{ - scheme: Protocol.scheme, - privileges: { - standard: true, - secure: true - } -}]); +protocol.registerSchemesAsPrivileged([ + { + scheme: Protocol.scheme, + privileges: { + standard: true, + secure: true, + }, + }, +]) // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. -app.on("ready", createWindow); +app.on('ready', createWindow) // Quit when all windows are closed. -app.on("window-all-closed", () => { +app.on('window-all-closed', () => { // On macOS it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q - if (process.platform !== "darwin") { - app.quit(); + if (process.platform !== 'darwin') { + app.quit() } else { - i18nextBackend.clearMainBindings(ipcMain); - ContextMenu.clearMainBindings(ipcMain); - SecureElectronLicenseKeys.clearMainBindings(ipcMain); + i18nextBackend.clearMainBindings(ipcMain) + ContextMenu.clearMainBindings(ipcMain) + SecureElectronLicenseKeys.clearMainBindings(ipcMain) } -}); +}) -app.on("activate", () => { +app.on('activate', () => { // On macOS 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 (win === null) { - createWindow(); + createWindow() } -}); +}) // https://electronjs.org/docs/tutorial/security#12-disable-or-limit-navigation -app.on("web-contents-created", (event, contents) => { - contents.on("will-navigate", (contentsEvent, navigationUrl) => { +app.on('web-contents-created', (event, contents) => { + contents.on('will-navigate', (contentsEvent, navigationUrl) => { /* eng-disable LIMIT_NAVIGATION_JS_CHECK */ - const parsedUrl = new URL(navigationUrl); - const validOrigins = [selfHost]; + const parsedUrl = new URL(navigationUrl) + const validOrigins = [selfHost] // Log and prevent the app from navigating to a new page if that page's origin is not whitelisted if (!validOrigins.includes(parsedUrl.origin)) { console.error( `The application tried to navigate to the following address: '${parsedUrl}'. This origin is not whitelisted and the attempt to navigate was blocked.` - ); + ) - contentsEvent.preventDefault(); + contentsEvent.preventDefault() } - }); + }) - contents.on("will-redirect", (contentsEvent, navigationUrl) => { - const parsedUrl = new URL(navigationUrl); - const validOrigins = []; + contents.on('will-redirect', (contentsEvent, navigationUrl) => { + const parsedUrl = new URL(navigationUrl) + const validOrigins = [] // Log and prevent the app from redirecting to a new page if (!validOrigins.includes(parsedUrl.origin)) { console.error( `The application tried to redirect to the following address: '${navigationUrl}'. This attempt was blocked.` - ); + ) - contentsEvent.preventDefault(); + contentsEvent.preventDefault() } - }); + }) // https://electronjs.org/docs/tutorial/security#11-verify-webview-options-before-creation - contents.on("will-attach-webview", (contentsEvent, webPreferences, params) => { - // Strip away preload scripts if unused or verify their location is legitimate - delete webPreferences.preload; - delete webPreferences.preloadURL; - - // Disable Node.js integration - webPreferences.nodeIntegration = false; - }); + contents.on( + 'will-attach-webview', + (contentsEvent, webPreferences, params) => { + // Strip away preload scripts if unused or verify their location is legitimate + delete webPreferences.preload + delete webPreferences.preloadURL + + // Disable Node.js integration + webPreferences.nodeIntegration = false + } + ) // https://electronjs.org/docs/tutorial/security#13-disable-or-limit-creation-of-new-windows // This code replaces the old "new-window" event handling; // https://github.com/electron/electron/pull/24517#issue-447670981 - contents.setWindowOpenHandler(({ - url - }) => { - const parsedUrl = new URL(url); - const validOrigins = []; + contents.setWindowOpenHandler(({ url }) => { + const parsedUrl = new URL(url) + const validOrigins = [] // Log and prevent opening up a new window if (!validOrigins.includes(parsedUrl.origin)) { @@ -274,21 +292,15 @@ app.on("web-contents-created", (event, contents) => { `The application tried to open a new window at the following address: '${url}'. This attempt was blocked.` ) return { - action: "deny" + action: 'deny', } } return { - action: "allow" - }; - }); -}); - - - - - - + action: 'allow', + } + }) +}) const initDMX = async () => { const serialPort = 'COM4' @@ -296,16 +308,16 @@ const initDMX = async () => { dmx = new DMX() const driver = new EnttecUSBDMXProDriver(serialPort, { dmxSpeed }) - + universe = await dmx.addUniverse('universe1', driver) - + universe.updateAll(0) } const initLP = async () => { lp = autoDetect({ debug: false }) - - lp.once('ready', ( device ) => { + + lp.once('ready', (device) => { console.log(`Connected to ${device}`) }) } @@ -313,25 +325,25 @@ const initLP = async () => { // initDMX() initLP() -lp.on('buttonDown', ( button ) => { +lp.on('buttonDown', (button) => { console.log(`Pressed => `, button) if (button.nr === 19) { - lp.allOff() - return + lp.allOff() + return } win.webContents.send('pad', { event: 'BUTTON_DOWN', button }) }) -lp.on('buttonUp', ( button ) => { +lp.on('buttonUp', (button) => { console.log('Released => ', button) - win.webContents.send('pad', { event: 'BUTTON_UP', button }) + win.webContents.send('pad', { event: 'BUTTON_UP', button }) }) ipcMain.on('lpClear', () => { - console.log("CLEAR") + console.log('CLEAR') lp.allOff() }) diff --git a/app/electron/menu.js b/app/electron/menu.js index d006692..a604f6d 100644 --- a/app/electron/menu.js +++ b/app/electron/menu.js @@ -1,12 +1,11 @@ -const { Menu, MenuItem, BrowserWindow } = require("electron"); -const i18nBackend = require("i18next-electron-fs-backend"); -const whitelist = require("../localization/whitelist"); -const isMac = process.platform === "darwin"; - -const MenuBuilder = function(mainWindow, appName) { +const { Menu, MenuItem, BrowserWindow } = require('electron') +const i18nBackend = require('i18next-electron-fs-backend') +const whitelist = require('../localization/whitelist') +const isMac = process.platform === 'darwin' +const MenuBuilder = function (mainWindow, appName) { // https://electronjs.org/docs/api/menu#main-process - const defaultTemplate = function(i18nextMainBackend) { + const defaultTemplate = function (i18nextMainBackend) { return [ // { role: "appMenu" } ...(isMac @@ -15,236 +14,239 @@ const MenuBuilder = function(mainWindow, appName) { label: appName, submenu: [ { - role: "about", - label: i18nextMainBackend.t("About") + role: 'about', + label: i18nextMainBackend.t('About'), }, { - type: "separator" + type: 'separator', }, { - role: "services", - label: i18nextMainBackend.t("Services") + role: 'services', + label: i18nextMainBackend.t('Services'), }, { - type: "separator" + type: 'separator', }, { - role: "hide", - label: i18nextMainBackend.t("Hide") + role: 'hide', + label: i18nextMainBackend.t('Hide'), }, { - role: "hideothers", - label: i18nextMainBackend.t("Hide Others") + role: 'hideothers', + label: i18nextMainBackend.t('Hide Others'), }, { - role: "unhide", - label: i18nextMainBackend.t("Unhide") + role: 'unhide', + label: i18nextMainBackend.t('Unhide'), }, { - type: "separator" + type: 'separator', }, { - role: "quit", - label: i18nextMainBackend.t("Quit") - } - ] - } + role: 'quit', + label: i18nextMainBackend.t('Quit'), + }, + ], + }, ] : []), // { role: "fileMenu" } { - label: i18nextMainBackend.t("File"), + label: i18nextMainBackend.t('File'), submenu: [ isMac ? { - role: "close", - label: i18nextMainBackend.t("Quit") + role: 'close', + label: i18nextMainBackend.t('Quit'), } : { - role: "quit", - label: i18nextMainBackend.t("Exit") - } - ] + role: 'quit', + label: i18nextMainBackend.t('Exit'), + }, + ], }, // { role: "editMenu" } { - label: i18nextMainBackend.t("Edit"), + label: i18nextMainBackend.t('Edit'), submenu: [ { - role: "undo", - label: i18nextMainBackend.t("Undo") + role: 'undo', + label: i18nextMainBackend.t('Undo'), }, { - role: "redo", - label: i18nextMainBackend.t("Redo") + role: 'redo', + label: i18nextMainBackend.t('Redo'), }, { - type: "separator" + type: 'separator', }, { - role: "cut", - label: i18nextMainBackend.t("Cut") + role: 'cut', + label: i18nextMainBackend.t('Cut'), }, { - role: "copy", - label: i18nextMainBackend.t("Copy") + role: 'copy', + label: i18nextMainBackend.t('Copy'), }, { - role: "paste", - label: i18nextMainBackend.t("Paste") + role: 'paste', + label: i18nextMainBackend.t('Paste'), }, ...(isMac ? [ { - role: "pasteAndMatchStyle", - label: i18nextMainBackend.t("Paste and Match Style") + role: 'pasteAndMatchStyle', + label: i18nextMainBackend.t('Paste and Match Style'), }, { - role: "delete", - label: i18nextMainBackend.t("Delete") + role: 'delete', + label: i18nextMainBackend.t('Delete'), }, { - role: "selectAll", - label: i18nextMainBackend.t("Select All") + role: 'selectAll', + label: i18nextMainBackend.t('Select All'), }, { - type: "separator" + type: 'separator', }, { - label: i18nextMainBackend.t("Speech"), + label: i18nextMainBackend.t('Speech'), submenu: [ { - role: "startspeaking", - label: i18nextMainBackend.t("Start Speaking") + role: 'startspeaking', + label: i18nextMainBackend.t('Start Speaking'), }, { - role: "stopspeaking", - label: i18nextMainBackend.t("Stop Speaking") - } - ] - } + role: 'stopspeaking', + label: i18nextMainBackend.t('Stop Speaking'), + }, + ], + }, ] : [ { - role: "delete", - label: i18nextMainBackend.t("Delete") + role: 'delete', + label: i18nextMainBackend.t('Delete'), }, { - type: "separator" + type: 'separator', }, { - role: "selectAll", - label: i18nextMainBackend.t("Select All") - } - ]) - ] + role: 'selectAll', + label: i18nextMainBackend.t('Select All'), + }, + ]), + ], }, // { role: "viewMenu" } { - label: i18nextMainBackend.t("View"), + label: i18nextMainBackend.t('View'), submenu: [ { - role: "reload", - label: i18nextMainBackend.t("Reload") + role: 'reload', + label: i18nextMainBackend.t('Reload'), }, { - role: "forcereload", - label: i18nextMainBackend.t("Force Reload") + role: 'forcereload', + label: i18nextMainBackend.t('Force Reload'), }, { - role: "toggledevtools", - label: i18nextMainBackend.t("Toggle Developer Tools") + role: 'toggledevtools', + label: i18nextMainBackend.t('Toggle Developer Tools'), }, { - type: "separator" + type: 'separator', }, { - role: "resetzoom", - label: i18nextMainBackend.t("Reset Zoom") + role: 'resetzoom', + label: i18nextMainBackend.t('Reset Zoom'), }, { - role: "zoomin", - label: i18nextMainBackend.t("Zoom In") + role: 'zoomin', + label: i18nextMainBackend.t('Zoom In'), }, { - role: "zoomout", - label: i18nextMainBackend.t("Zoom Out") + role: 'zoomout', + label: i18nextMainBackend.t('Zoom Out'), }, { - type: "separator" + type: 'separator', }, { - role: "togglefullscreen", - label: i18nextMainBackend.t("Toggle Fullscreen") - } - ] + role: 'togglefullscreen', + label: i18nextMainBackend.t('Toggle Fullscreen'), + }, + ], }, // language menu { - label: i18nextMainBackend.t("Language"), - submenu: whitelist.buildSubmenu(i18nBackend.changeLanguageRequest, i18nextMainBackend) + label: i18nextMainBackend.t('Language'), + submenu: whitelist.buildSubmenu( + i18nBackend.changeLanguageRequest, + i18nextMainBackend + ), }, // { role: "windowMenu" } { - label: i18nextMainBackend.t("Window"), + label: i18nextMainBackend.t('Window'), submenu: [ { - role: "minimize", - label: i18nextMainBackend.t("Minimize") + role: 'minimize', + label: i18nextMainBackend.t('Minimize'), }, { - role: "zoom", - label: i18nextMainBackend.t("Zoom") + role: 'zoom', + label: i18nextMainBackend.t('Zoom'), }, ...(isMac ? [ { - type: "separator" + type: 'separator', }, { - role: "front", - label: i18nextMainBackend.t("Front") + role: 'front', + label: i18nextMainBackend.t('Front'), }, { - type: "separator" + type: 'separator', }, { - role: "window", - label: i18nextMainBackend.t("Window") - } + role: 'window', + label: i18nextMainBackend.t('Window'), + }, ] : [ { - role: "close", - label: i18nextMainBackend.t("Close") - } - ]) - ] + role: 'close', + label: i18nextMainBackend.t('Close'), + }, + ]), + ], }, { - role: "help", - label: i18nextMainBackend.t("Help"), + role: 'help', + label: i18nextMainBackend.t('Help'), submenu: [ { - label: i18nextMainBackend.t("Learn More"), + label: i18nextMainBackend.t('Learn More'), click: async () => { - const { shell } = require("electron"); - await shell.openExternal("https://electronjs.org"); - } - } - ] - } - ]; - }; + const { shell } = require('electron') + await shell.openExternal('https://electronjs.org') + }, + }, + ], + }, + ] + } return { - buildMenu: function(i18nextMainBackend) { - const menu = Menu.buildFromTemplate(defaultTemplate(i18nextMainBackend)); - Menu.setApplicationMenu(menu); + buildMenu: function (i18nextMainBackend) { + const menu = Menu.buildFromTemplate(defaultTemplate(i18nextMainBackend)) + Menu.setApplicationMenu(menu) - return menu; - } - }; -}; + return menu + }, + } +} -module.exports = MenuBuilder; +module.exports = MenuBuilder diff --git a/app/electron/preload.js b/app/electron/preload.js index 5b59f8e..ed0b196 100644 --- a/app/electron/preload.js +++ b/app/electron/preload.js @@ -1,11 +1,11 @@ -const { contextBridge, ipcRenderer } = require("electron"); -const fs = require("fs"); -const i18nextBackend = require("i18next-electron-fs-backend"); -const Store = require("secure-electron-store").default; -const ContextMenu = require("secure-electron-context-menu").default; -const SecureElectronLicenseKeys = require("secure-electron-license-keys"); +const { contextBridge, ipcRenderer } = require('electron') +const fs = require('fs') +const i18nextBackend = require('i18next-electron-fs-backend') +const Store = require('secure-electron-store').default +const ContextMenu = require('secure-electron-context-menu').default +const SecureElectronLicenseKeys = require('secure-electron-license-keys') -const store = new Store(); +const store = new Store() const API = { i18nextElectronBackend: i18nextBackend.preloadBindings(ipcRenderer, process), @@ -15,16 +15,16 @@ const API = { send: (channel, data) => { // let validChannels = ["toMain"]; // if (validChannels.includes(channel)) { - ipcRenderer.send(channel, data); + ipcRenderer.send(channel, data) // } }, receive: (channel, func) => { // let validChannels = ["fromMain"]; // if (validChannels.includes(channel)) { - // Deliberately strip event as it includes `sender` - ipcRenderer.on(channel, (event, ...args) => func(...args)); + // Deliberately strip event as it includes `sender` + ipcRenderer.on(channel, (event, ...args) => func(...args)) // } - } + }, } -contextBridge.exposeInMainWorld("api", API) +contextBridge.exposeInMainWorld('api', API) diff --git a/app/electron/protocol.js b/app/electron/protocol.js index eed7e91..44f4971 100644 --- a/app/electron/protocol.js +++ b/app/electron/protocol.js @@ -12,60 +12,60 @@ Implementing a custom protocol achieves two goals: 2) Avoids running the app in a file:// origin */ -const fs = require("fs"); -const path = require("path"); +const fs = require('fs') +const path = require('path') -const DIST_PATH = path.join(__dirname, "../../app/dist"); -const scheme = "app"; +const DIST_PATH = path.join(__dirname, '../../app/dist') +const scheme = 'app' const mimeTypes = { - ".js": "text/javascript", - ".mjs": "text/javascript", - ".html": "text/html", - ".htm": "text/html", - ".json": "application/json", - ".css": "text/css", - ".svg": "image/svg+xml", - ".ico": "image/vnd.microsoft.icon", - ".png": "image/png", - ".jpg": "image/jpeg", - ".map": "text/plain" -}; + '.js': 'text/javascript', + '.mjs': 'text/javascript', + '.html': 'text/html', + '.htm': 'text/html', + '.json': 'application/json', + '.css': 'text/css', + '.svg': 'image/svg+xml', + '.ico': 'image/vnd.microsoft.icon', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.map': 'text/plain', +} function charset(mimeExt) { - return [".html", ".htm", ".js", ".mjs"].some((m) => m === mimeExt) ? - "utf-8" : - null; + return ['.html', '.htm', '.js', '.mjs'].some((m) => m === mimeExt) + ? 'utf-8' + : null } function mime(filename) { - const mimeExt = path.extname(`${filename || ""}`).toLowerCase(); - const mimeType = mimeTypes[mimeExt]; - return mimeType ? { mimeExt, mimeType } : { mimeExt: null, mimeType: null }; + const mimeExt = path.extname(`${filename || ''}`).toLowerCase() + const mimeType = mimeTypes[mimeExt] + return mimeType ? { mimeExt, mimeType } : { mimeExt: null, mimeType: null } } function requestHandler(req, next) { - const reqUrl = new URL(req.url); - let reqPath = path.normalize(reqUrl.pathname); - if (reqPath === "/") { - reqPath = "/index.html"; + const reqUrl = new URL(req.url) + let reqPath = path.normalize(reqUrl.pathname) + if (reqPath === '/') { + reqPath = '/index.html' } - const reqFilename = path.basename(reqPath); + const reqFilename = path.basename(reqPath) fs.readFile(path.join(DIST_PATH, reqPath), (err, data) => { - const { mimeExt, mimeType } = mime(reqFilename); + const { mimeExt, mimeType } = mime(reqFilename) if (!err && mimeType !== null) { next({ mimeType, charset: charset(mimeExt), - data - }); + data, + }) } else { - console.error(err); + console.error(err) } - }); + }) } module.exports = { scheme, - requestHandler -}; \ No newline at end of file + requestHandler, +} diff --git a/app/src/components/Pad.tsx b/app/src/components/Pad.tsx index bc9901c..1c61f36 100644 --- a/app/src/components/Pad.tsx +++ b/app/src/components/Pad.tsx @@ -12,7 +12,10 @@ const Pad = ({ x, y }) => { const dispatch = useDispatch() const { isPressed, color } = useSelector((state) => state.pad.buttons[button]) - useEffect(() => window.api.send(CHANNELS.LP.PAD_COLOR, { button, color }), [color]) + useEffect( + () => window.api.send(CHANNELS.LP.PAD_COLOR, { button, color }), + [color] + ) useEffect(() => { console.log('INVOKE PRESSED =>', button, 'STATE: ', isPressed) diff --git a/app/src/components/Receiver.tsx b/app/src/components/Receiver.tsx new file mode 100644 index 0000000..7f45d9f --- /dev/null +++ b/app/src/components/Receiver.tsx @@ -0,0 +1,16 @@ +import { useDispatch } from 'react-redux' +import { randomRGB } from '../utils/color' +import { changeColor, setPressed } from '../redux/components/pad/padActions' + +const Receiver = () => { + const dispatch = useDispatch() + + console.log('DSDSDS') + // @ts-ignore + window.api.receive(CHANNELS.LP.PAD, ({ event, button }) => { + dispatch(setPressed(button, event === 'BUTTON_DOWN' ?? false)) + dispatch(changeColor(button, randomRGB())) + }) +} + +export default Receiver diff --git a/app/src/components/subitem/subitem.jsx b/app/src/components/subitem/subitem.jsx index 370b922..c52c075 100644 --- a/app/src/components/subitem/subitem.jsx +++ b/app/src/components/subitem/subitem.jsx @@ -1,23 +1,29 @@ -import React from "react"; +import React from 'react' class SubItem extends React.Component { constructor(props) { - super(props); + super(props) } componentWillUnmount() { - window.api.contextMenu.clearRendererBindings(); + window.api.contextMenu.clearRendererBindings() } componentDidMount() { // Set up binding in code whenever the context menu item // of id "alert" is selected - window.api.contextMenu.onReceive("softAlert", function(args) { - console.log(`This alert was brought to you by secure-electron-context-menu by ${args.attributes.name}`); + window.api.contextMenu.onReceive( + 'softAlert', + function (args) { + console.log( + `This alert was brought to you by secure-electron-context-menu by ${args.attributes.name}` + ) - // Note - we have access to the "params" object as defined here: https://www.electronjs.org/docs/api/web-contents#event-context-menu - // args.params - }, this.props.id); + // Note - we have access to the "params" object as defined here: https://www.electronjs.org/docs/api/web-contents#event-context-menu + // args.params + }, + this.props.id + ) } render() { @@ -30,8 +36,8 @@ class SubItem extends React.Component { ID ({this.props.id}): Try right-clicking me for a custom context menu - ); + ) } } -export default SubItem; +export default SubItem diff --git a/app/src/constants/ipc.ts b/app/src/constants/ipc.ts index 32bea5f..1b9af62 100644 --- a/app/src/constants/ipc.ts +++ b/app/src/constants/ipc.ts @@ -2,9 +2,9 @@ export const CHANNELS = { LP: { CLEAR: 'lpClear', PAD: 'pad', - PAD_COLOR: 'lpPadColor' + PAD_COLOR: 'lpPadColor', }, DMX: { - CLEAR: 'dmxClear' - } + CLEAR: 'dmxClear', + }, } diff --git a/app/src/core/nav.jsx b/app/src/core/nav.jsx index 83e5f40..81f6e5b 100644 --- a/app/src/core/nav.jsx +++ b/app/src/core/nav.jsx @@ -1,14 +1,14 @@ -import React from "react"; -import ROUTES from "Constants/routes"; -import { useNavigate } from "react-router-dom"; +import React from 'react' +import ROUTES from 'Constants/routes' +import { useNavigate } from 'react-router-dom' import { validateLicenseRequest, validateLicenseResponse, -} from "secure-electron-license-keys"; +} from 'secure-electron-license-keys' class Nav extends React.Component { constructor(props) { - super(props); + super(props) this.state = { mobileMenuActive: false, @@ -16,25 +16,25 @@ class Nav extends React.Component { // license-specific licenseValid: false, - allowedMajorVersions: "", - allowedMinorVersions: "", - appVersion: "", - licenseExpiry: "", - }; + allowedMajorVersions: '', + allowedMinorVersions: '', + appVersion: '', + licenseExpiry: '', + } - this.toggleMenu = this.toggleMenu.bind(this); - this.toggleLicenseModal = this.toggleLicenseModal.bind(this); - this.navigate = this.navigate.bind(this); + this.toggleMenu = this.toggleMenu.bind(this) + this.toggleLicenseModal = this.toggleLicenseModal.bind(this) + this.navigate = this.navigate.bind(this) } componentWillUnmount() { - window.api.licenseKeys.clearRendererBindings(); + window.api.licenseKeys.clearRendererBindings() } componentDidMount() { // Set up binding to listen when the license key is // validated by the main process - const _ = this; + const _ = this window.api.licenseKeys.onReceive(validateLicenseResponse, function (data) { // If the license key/data is valid @@ -50,33 +50,33 @@ class Nav extends React.Component { allowedPatchVersions: data.patch, appVersion: data.appVersion, licenseExpiry: data.expire, - }); + }) } else { _.setState({ licenseValid: false, - }); + }) } - }); + }) } toggleMenu(_event) { this.setState({ mobileMenuActive: !this.state.mobileMenuActive, - }); + }) } toggleLicenseModal(_event) { - const previous = this.state.licenseModalActive; + const previous = this.state.licenseModalActive // Only send license request if the modal // is not already open if (!previous) { - window.api.licenseKeys.send(validateLicenseRequest); + window.api.licenseKeys.send(validateLicenseRequest) } this.setState({ licenseModalActive: !this.state.licenseModalActive, - }); + }) } // Using a custom method to navigate because we @@ -88,15 +88,15 @@ class Nav extends React.Component { mobileMenuActive: false, }, function () { - this.props.navigate(url); + this.props.navigate(url) } - ); + ) } renderLicenseModal() { return (
- DMX-LP-Controller Interface -
-- Use Launchpad (MIDI) as a Controller to manage your DMX Lighting Fixtures. -
-DMX-LP-Controller Interface
++ Use Launchpad (MIDI) as a Controller to manage your DMX Lighting + Fixtures. +