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 (
+ className={`modal ${this.state.licenseModalActive ? 'is-active' : ''}`}>
{this.state.licenseValid ? ( @@ -104,16 +104,16 @@ class Nav extends React.Component { The license key for this product has been validated and the following versions of this app are allowed for your use:
- Major versions:{" "} + Major versions:{' '} {this.state.allowedMajorVersions}
- Minor versions:{" "} + Minor versions:{' '} {this.state.allowedMinorVersions}
- Patch versions:{" "} + Patch versions:{' '} {this.state.allowedPatchVersions}
- Expires on:{" "} + Expires on:{' '} {!this.state.licenseExpiry - ? "never!" - : this.state.licenseExpiry}{" "} + ? 'never!' + : this.state.licenseExpiry}{' '}
( App version: @@ -128,7 +128,7 @@ class Nav extends React.Component {
The license key is not valid.
If you'd like to create a license key, follow these steps: -
    +
    1. Install this package globally ( npm i secure-electron-license-keys-cli -g). @@ -137,7 +137,7 @@ class Nav extends React.Component { Run secure-electron-license-keys-cli.
    2. - Copy public.key and{" "} + Copy public.key and{' '} license.data into the root folder of this app.
    3. @@ -146,7 +146,7 @@ class Nav extends React.Component {
    4. If you'd like to further customize your license keys, copy - this link into your browser:{" "} + this link into your browser:{' '} https://github.com/reZach/secure-electron-license-keys-cli @@ -162,7 +162,7 @@ class Nav extends React.Component { aria-label="close" onClick={this.toggleLicenseModal}>
- ); + ) } render() { @@ -175,7 +175,7 @@ class Nav extends React.Component {
- ); + ) } } -function WithNavigate(props){ - const navigate = useNavigate(); +function WithNavigate(props) { + const navigate = useNavigate() return
- ); + ) } } -export default ContextMenu; +export default ContextMenu diff --git a/app/src/pages/home/home.tsx b/app/src/pages/home/home.tsx index 80c3ef6..77db790 100644 --- a/app/src/pages/home/home.tsx +++ b/app/src/pages/home/home.tsx @@ -1,36 +1,36 @@ -import { Link } from "react-router-dom"; -import React from "react" -import { Button } from "@material-ui/core"; -import Test from "../../components/Test"; +import { Link } from 'react-router-dom' +import React from 'react' +import { Button } from '@material-ui/core' +import Test from '../../components/Test' const Home = () => { return ( -
-
-
-
-

- DMX-LP-Controller Interface -

-

- Use Launchpad (MIDI) as a Controller to manage your DMX Lighting Fixtures. -

-
-
-
-
-
-
-

Menu

-
- Launchpad
- {/* */} +
+
+
+
+

DMX-LP-Controller Interface

+

+ Use Launchpad (MIDI) as a Controller to manage your DMX Lighting + Fixtures. +

+
+
+
+
+
+

Menu

+
+ Launchpad +
+ {/* */}
-
- +
+
+
) } -export default Home \ No newline at end of file +export default Home diff --git a/app/src/pages/launchpad/launchpad.tsx b/app/src/pages/launchpad/launchpad.tsx index 5e943b5..843666e 100644 --- a/app/src/pages/launchpad/launchpad.tsx +++ b/app/src/pages/launchpad/launchpad.tsx @@ -23,25 +23,6 @@ const createGrid = () => { } const Launchpad = (props) => { - const dispatch = useDispatch() - - // @ts-ignore - window.api.receive(CHANNELS.LP.PAD, ({ event, button }) => { - dispatch( - setPressed({ - pressed: event === 'BUTTON_DOWN' ?? false, - button: button.nr, - }) - ) - - dispatch( - changeColor({ - color: randomRGB(), - button: button.nr, - }) - ) - }) - // @ts-ignore window.api.send(CHANNELS.LP.CLEAR) diff --git a/app/src/pages/localization/localization.css b/app/src/pages/localization/localization.css index 60bb7a6..7746589 100644 --- a/app/src/pages/localization/localization.css +++ b/app/src/pages/localization/localization.css @@ -1,3 +1,3 @@ .italics { - font-style: italic; -} \ No newline at end of file + font-style: italic; +} diff --git a/app/src/pages/localization/localization.jsx b/app/src/pages/localization/localization.jsx index b885605..cab69d4 100644 --- a/app/src/pages/localization/localization.jsx +++ b/app/src/pages/localization/localization.jsx @@ -1,23 +1,23 @@ -import React from "react"; -import { withTranslation } from "react-i18next"; -import "./localization.css"; +import React from 'react' +import { withTranslation } from 'react-i18next' +import './localization.css' class Localization extends React.Component { render() { - const { t } = this.props; + const { t } = this.props return (
-

{t("Hello")}

+

{t('Hello')}

Try changing the language in the menu bar!
- ); + ) } } -export default withTranslation()(Localization); +export default withTranslation()(Localization) diff --git a/app/src/redux/store/store.ts b/app/src/redux/store/store.ts index 22a4cf6..5d80208 100644 --- a/app/src/redux/store/store.ts +++ b/app/src/redux/store/store.ts @@ -1,9 +1,9 @@ -import { combineReducers } from "redux" -import { configureStore, getDefaultMiddleware } from "@reduxjs/toolkit" -import { createHashHistory } from "history" -import { createReduxHistoryContext } from "redux-first-history" -import logger from "redux-logger" -import padReducer from "../components/pad/padReducer" +import { combineReducers } from 'redux' +import { configureStore, getDefaultMiddleware } from '@reduxjs/toolkit' +import { createHashHistory } from 'history' +import { createReduxHistoryContext } from 'redux-first-history' +import logger from 'redux-logger' +import padReducer from '../components/pad/padReducer' const { routerMiddleware, createReduxHistory, routerReducer } = createReduxHistoryContext({ @@ -20,7 +20,7 @@ export const store = configureStore({ serializableCheck: false, }), routerMiddleware, - logger + logger, ], }) diff --git a/app/src/utils/color.ts b/app/src/utils/color.ts index f6cad2e..94b85a9 100644 --- a/app/src/utils/color.ts +++ b/app/src/utils/color.ts @@ -1,10 +1,10 @@ -import { RgbColor } from "launchpad.js"; +import { RgbColor } from 'launchpad.js' export const randomRGB = (): RgbColor => { - const num = Math.round(0xffffff * Math.random()); - const r = num >> 16; - const g = num >> 8 & 255; - const b = num & 255; + const num = Math.round(0xffffff * Math.random()) + const r = num >> 16 + const g = (num >> 8) & 255 + const b = num & 255 return [r, g, b] } @@ -12,8 +12,9 @@ export const randomRGB = (): RgbColor => { export const ColorOff = [0, 0, 0] as RgbColor const componentToHex = (c: number): string => { - var hex = c.toString(16); - return hex.length == 1 ? "0" + hex : hex; + var hex = c.toString(16) + return hex.length == 1 ? '0' + hex : hex } -export const rgbToHex = ([r, g, b]: RgbColor) => "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); +export const rgbToHex = ([r, g, b]: RgbColor) => + '#' + componentToHex(r) + componentToHex(g) + componentToHex(b) diff --git a/app/src/utils/index.ts b/app/src/utils/index.ts index c1ba61e..74eae9c 100644 --- a/app/src/utils/index.ts +++ b/app/src/utils/index.ts @@ -8,4 +8,5 @@ export const getAllButtons = () => { return buttons } -export const arrayEqual = (arr1: [], arr2: []) => JSON.stringify(arr1) === JSON.stringify(arr2) \ No newline at end of file +export const arrayEqual = (arr1: [], arr2: []) => + JSON.stringify(arr1) === JSON.stringify(arr2) diff --git a/dev-scripts/create-nonce.js b/dev-scripts/create-nonce.js index 435bb59..b07db7d 100644 --- a/dev-scripts/create-nonce.js +++ b/dev-scripts/create-nonce.js @@ -1,5 +1,5 @@ -const { v4: uuidv4 } = require('uuid'); +const { v4: uuidv4 } = require('uuid') -module.exports = function() { - return new Buffer(uuidv4()).toString('base64'); -}; \ No newline at end of file +module.exports = function () { + return new Buffer(uuidv4()).toString('base64') +} diff --git a/dev-scripts/launchDevServer.js b/dev-scripts/launchDevServer.js index 4fdab8c..c99d8a4 100644 --- a/dev-scripts/launchDevServer.js +++ b/dev-scripts/launchDevServer.js @@ -1,74 +1,79 @@ -const fs = require("fs"); -const { - exec -} = require("child_process"); -const logFilePath = "./dev-scripts/webpack-dev-server.log"; -const errorLogFilePath = "./dev-scripts/webpack-dev-server-error.log"; -const interval = 100; -const showHint = 600 * 3; // show hint after 3 minutes (60 sec * 3) -let hintCounter = 1; +const fs = require('fs') +const { exec } = require('child_process') +const logFilePath = './dev-scripts/webpack-dev-server.log' +const errorLogFilePath = './dev-scripts/webpack-dev-server-error.log' +const interval = 100 +const showHint = 600 * 3 // show hint after 3 minutes (60 sec * 3) +let hintCounter = 1 // Poll webpack-dev-server.log until the webpack bundle has compiled successfully const intervalId = setInterval(function () { try { if (fs.existsSync(logFilePath)) { const log = fs.readFileSync(logFilePath, { - encoding: "utf8" - }); + encoding: 'utf8', + }) // "compiled successfully" is the string we need to find // to know that webpack is done bundling everything and we // can load our Electron app with no issues. We split up the // validation because the output contains non-standard characters. - const compiled = log.indexOf("compiled"); - if (compiled >= 0 && log.indexOf("successfully", compiled) >= 0) { - console.log("Webpack development server is ready, launching Electron app."); - clearInterval(intervalId); + const compiled = log.indexOf('compiled') + if (compiled >= 0 && log.indexOf('successfully', compiled) >= 0) { + console.log( + 'Webpack development server is ready, launching Electron app.' + ) + clearInterval(intervalId) // Start our electron app - const electronProcess = exec("cross-env NODE_ENV=development electron ."); - electronProcess.stdout.on("data", function(data) { - process.stdout.write(data); - }); - electronProcess.stderr.on("data", function(data) { - process.stdout.write(data); - }); - } else if (log.indexOf("Module build failed") >= 0) { - + const electronProcess = exec( + 'cross-env NODE_ENV=development electron .' + ) + electronProcess.stdout.on('data', function (data) { + process.stdout.write(data) + }) + electronProcess.stderr.on('data', function (data) { + process.stdout.write(data) + }) + } else if (log.indexOf('Module build failed') >= 0) { if (fs.existsSync(errorLogFilePath)) { const errorLog = fs.readFileSync(errorLogFilePath, { - encoding: "utf8" - }); + encoding: 'utf8', + }) - console.log(errorLog); - console.log(`Webpack failed to compile; this error has also been logged to '${errorLogFilePath}'.`); - clearInterval(intervalId); + console.log(errorLog) + console.log( + `Webpack failed to compile; this error has also been logged to '${errorLogFilePath}'.` + ) + clearInterval(intervalId) - return process.exit(1); + return process.exit(1) } else { - console.log("Webpack failed to compile, but the error is unknown.") - clearInterval(intervalId); + console.log('Webpack failed to compile, but the error is unknown.') + clearInterval(intervalId) - return process.exit(1); + return process.exit(1) } } else { - hintCounter++; + hintCounter++ // Show hint so user is not waiting/does not know where to // look for an error if it has been thrown and/or we are stuck - if (hintCounter > showHint){ - console.error(`Webpack is likely failing for an unknown reason, please check '${errorLogFilePath}' for more details.`); - clearInterval(intervalId); + if (hintCounter > showHint) { + console.error( + `Webpack is likely failing for an unknown reason, please check '${errorLogFilePath}' for more details.` + ) + clearInterval(intervalId) - return process.exit(1); + return process.exit(1) } } } } catch (error) { // Exit with an error code - console.error("Webpack or electron fatal error" + error); - clearInterval(intervalId); + console.error('Webpack or electron fatal error' + error) + clearInterval(intervalId) - return process.exit(1); + return process.exit(1) } -}, interval); \ No newline at end of file +}, interval) diff --git a/dev-scripts/prepareDevServer.js b/dev-scripts/prepareDevServer.js index 95b3d70..3229d83 100644 --- a/dev-scripts/prepareDevServer.js +++ b/dev-scripts/prepareDevServer.js @@ -1,25 +1,25 @@ -const fs = require("fs"); -const { - exec -} = require("child_process"); -const logFilePath = "./dev-scripts/webpack-dev-server.log"; -const errorLogFilePath = "./dev-scripts/webpack-dev-server-error.log"; +const fs = require('fs') +const { exec } = require('child_process') +const logFilePath = './dev-scripts/webpack-dev-server.log' +const errorLogFilePath = './dev-scripts/webpack-dev-server-error.log' -console.log(`Preparing webpack development server. (Logging webpack output to '${logFilePath}')`); +console.log( + `Preparing webpack development server. (Logging webpack output to '${logFilePath}')` +) // Delete the old webpack-dev-server.log if it is present try { - fs.unlinkSync(logFilePath); + fs.unlinkSync(logFilePath) } catch (error) { // Existing webpack-dev-server log file may not exist } // Delete the old webpack-dev-server-error.log if it is present try { - fs.unlinkSync(errorLogFilePath); + fs.unlinkSync(errorLogFilePath) } catch (error) { // Existing webpack-dev-server-error log file may not exist } // Start the webpack development server -exec("npm run dev-server"); \ No newline at end of file +exec('npm run dev-server')