From 25d0f50f3da69c2f59b27f5640d54315202e4c0b Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Mon, 5 Apr 2021 04:52:22 +0300 Subject: [PATCH 1/5] Create readme.md --- providers/prompt/readme.md | 76 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 providers/prompt/readme.md diff --git a/providers/prompt/readme.md b/providers/prompt/readme.md new file mode 100644 index 00000000..2eb13b74 --- /dev/null +++ b/providers/prompt/readme.md @@ -0,0 +1,76 @@ +# Prompt Documentation + +

prompt-preview

+ +## Usage + +```js +prompt([options, parentBrowserWindow]).then(...).catch(...) +``` + +## Example + +```js +const prompt = require('./providers/prompt'); + +prompt({ + title: 'Prompt example', + label: 'URL:', + value: 'http://example.org', + inputAttrs: { + type: 'url' + }, + type: 'input' +}) +.then((r) => { + if(r === null) { + console.log('user cancelled'); + } else { + console.log('result', r); + } +}) +.catch(console.error); +``` + +### Options object (optional) + +| Key | Explanation | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| title | (optional, string) The title of the prompt window. Defaults to 'Prompt'. | +| label | (optional, string) The label which appears on the prompt for the input field. Defaults to 'Please input a value:'. | +| buttonLabels | (optional, object) The text for the OK/cancel buttons. Properties are 'ok' and 'cancel'. Defaults to null. | +| value | (optional, string) The default value for the input field. Defaults to null. | +| type | (optional, string) The type of input field, either 'input' for a standard text input field or 'select' for a dropdown type input. Defaults to 'input'. | +| inputAttrs | (optional, object) The attributes of the input field, analagous to the HTML attributes: `{type: 'text', required: true}` -> ``. Used if the type is 'input' | +| selectOptions | (optional, object) The items for the select dropdown if using the 'select' type in the format 'value': 'display text', where the value is what will be given to the then block and the display text is what the user will see. | +| useHtmlLabel | (optional, boolean) Whether the label should be interpreted as HTML or not. Defaults to false. | +| width | (optional, integer) The width of the prompt window. Defaults to 370. | +| minWidth | (optional, integer) The minimum allowed width for the prompt window. Same default value as width. | +| height | (optional, integer) The height of the prompt window. Defaults to 130. | +| minHeight | (optional, integer) The minimum allowed height for the prompt window. Same default value as height. | +| resizable | (optional, boolean) Whether the prompt window can be resized or not (also sets useContentSize). Defaults to false. | +| alwaysOnTop | (optional, boolean) Whether the window should always stay on top of other windows. Defaults to false | +| icon | (optional, string) The path to an icon image to use in the title bar. Defaults to null and uses electron's icon. | +| customStylesheet | (optional, string) The local path of a CSS file to stylize the prompt window. Defaults to null. | +| menuBarVisible | (optional, boolean) Whether to show the menubar or not. Defaults to false. | +| skipTaskbar | (optional, boolean) Whether to show the prompt window icon in taskbar. Defaults to true. | +| frame | (optional, boolean) Wether to create prompt with frame. Defaults to true. | +| customScript | (optional, string) The local path of a JS file to run on preload. Defaults to null. | +| enableRemoteModule | (optional, boolean) Wether the prompt window have remote modules activated, Defaults to false. | + +If not supplied, it uses the defaults listed in the table above. + +### parentBrowserWindow (optional) + +The window in which to display the prompt on. If not supplied, the parent window of the prompt will be null. + +### customScript (optional) + +Create the script with the following template: + +```node +module.exports = () => { + // This function will be called as a preload script + // So you can use front features like `document.querySelector` +}; +``` From a215035d0792891845eeb625ba140fd3dadfb241 Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Mon, 5 Apr 2021 18:28:27 +0300 Subject: [PATCH 2/5] refactor and css fix --- providers/prompt/customTitlebar.js | 15 +++++---------- providers/prompt/darkPrompt.css | 4 +++- providers/prompt/index.js | 6 +++--- 3 files changed, 11 insertions(+), 14 deletions(-) diff --git a/providers/prompt/customTitlebar.js b/providers/prompt/customTitlebar.js index 116aa56c..6c508210 100644 --- a/providers/prompt/customTitlebar.js +++ b/providers/prompt/customTitlebar.js @@ -5,15 +5,10 @@ module.exports = () => { backgroundColor: customTitlebar.Color.fromHex("#050505"), minimizable: false, maximizable: false, - unfocusEffect: true, + menu: null }); - try { - bar.updateMenu(null); - } catch (e) { - //will always throw type error - null isn't menu, but it works - } - let container = document.querySelector('#container'); - container.style.width = '100%'; - container.style.position = 'fixed'; - container.style.border = 'unset'; + let mainStyle = document.querySelector('#container').style; + mainStyle.width = '100%'; + mainStyle.position = 'fixed'; + mainStyle.border = 'unset'; } \ No newline at end of file diff --git a/providers/prompt/darkPrompt.css b/providers/prompt/darkPrompt.css index 8b343a25..49e0f88e 100644 --- a/providers/prompt/darkPrompt.css +++ b/providers/prompt/darkPrompt.css @@ -34,7 +34,9 @@ body { } #ok:hover, -#cancel:hover { +#ok:focus, +#cancel:hover, +#cancel:focus { outline: rgba(60, 0, 0, 0.4) solid 2px; } diff --git a/providers/prompt/index.js b/providers/prompt/index.js index d6664305..0d89ebe9 100644 --- a/providers/prompt/index.js +++ b/providers/prompt/index.js @@ -50,9 +50,9 @@ function electronPrompt(options, parentWindow) { minWidth: options_.minWidth, minHeight: options_.minHeight, resizable: options_.resizable, - minimizable: false, - fullscreenable: false, - maximizable: false, + minimizable: !options_.skipTaskbar && !parentWindow && !options_.alwaysOnTop, + fullscreenable: options_.resizable, + maximizable: options_.resizable, parent: parentWindow, skipTaskbar: options_.skipTaskbar, alwaysOnTop: options_.alwaysOnTop, From 724c213af10c4ade43fb15b564fbb12123672ffb Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Mon, 5 Apr 2021 23:18:54 +0300 Subject: [PATCH 3/5] xo --fix --- menu.js | 4 +- package.json | 12 ++- plugins/in-app-menu/back.js | 9 +- providers/prompt/custom-titlebar.js | 14 +++ providers/prompt/customTitlebar.js | 14 --- .../{darkPrompt.css => dark-prompt.css} | 0 providers/prompt/index.js | 30 +++---- providers/prompt/page/prompt.js | 88 +++++++++---------- 8 files changed, 89 insertions(+), 82 deletions(-) create mode 100644 providers/prompt/custom-titlebar.js delete mode 100644 providers/prompt/customTitlebar.js rename providers/prompt/{darkPrompt.css => dark-prompt.css} (100%) diff --git a/menu.js b/menu.js index 3754d14c..c0495d68 100644 --- a/menu.js +++ b/menu.js @@ -321,13 +321,13 @@ function setProxy(item, win) { }, type: 'input', icon: iconPath, - customStylesheet: path.join(__dirname, "providers", "prompt", "darkPrompt.css"), + customStylesheet: path.join(__dirname, "providers", "prompt", "dark-prompt.css"), }; //TODO: custom bar on prompt need testing on macOS if(!is.macOS()) { Object.assign(options, { frame: false, - customScript: path.join(__dirname, "providers", "prompt", "customTitlebar.js"), + customScript: path.join(__dirname, "providers", "prompt", "custom-titlebar.js"), enableRemoteModule: true, height: 200, width: 450, diff --git a/package.json b/package.json index 36591897..144ac53f 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,16 @@ "envs": [ "node", "browser" - ] + ], + "rules": { + "quotes": [ + "error", + "double", + { + "avoidEscape": true, + "allowTemplateLiterals": true + } + ] + } } } diff --git a/plugins/in-app-menu/back.js b/plugins/in-app-menu/back.js index b17ee032..938c5475 100644 --- a/plugins/in-app-menu/back.js +++ b/plugins/in-app-menu/back.js @@ -9,8 +9,6 @@ const { injectCSS } = require("../utils"); //check that menu doesn't get created twice let calledReadyToShow = false; -//check menu state isn't changed twice -let calledFinishedLoad = false //tracks menu visibility let visible = true; // win hook for fixing menu @@ -37,6 +35,7 @@ module.exports = (winImport) => { if (calledReadyToShow) { return; } + calledReadyToShow = true; setApplicationMenu(win); @@ -53,7 +52,7 @@ module.exports = (winImport) => { win.webContents.on("did-finish-load", () => { // fix bug with menu not applying on start when no internet connection available setMenuVisibility(!config.get("options.hideMenu")); - }) + }); }; function switchMenuVisibility() { @@ -81,10 +80,10 @@ function updateTemplate(template) { for (let item of template) { // Change onClick of checkbox+radio if ((item.type === "checkbox" || item.type === "radio") && !item.fixed) { - let originalOnclick = item.click; + const originalOnclick = item.click; item.click = (itemClicked) => { originalOnclick(itemClicked); - updateCheckboxesAndRadioButtons(itemClicked, item.type === 'radio', item.hasSubmenu); + updateCheckboxesAndRadioButtons(itemClicked, item.type === "radio", item.hasSubmenu); }; item.fixed = true; } diff --git a/providers/prompt/custom-titlebar.js b/providers/prompt/custom-titlebar.js new file mode 100644 index 00000000..c36ce5f5 --- /dev/null +++ b/providers/prompt/custom-titlebar.js @@ -0,0 +1,14 @@ +const customTitlebar = require("custom-electron-titlebar"); + +module.exports = () => { + const bar = new customTitlebar.Titlebar({ + backgroundColor: customTitlebar.Color.fromHex("#050505"), + minimizable: false, + maximizable: false, + menu: null + }); + const mainStyle = document.querySelector("#container").style; + mainStyle.width = "100%"; + mainStyle.position = "fixed"; + mainStyle.border = "unset"; +}; diff --git a/providers/prompt/customTitlebar.js b/providers/prompt/customTitlebar.js deleted file mode 100644 index 6c508210..00000000 --- a/providers/prompt/customTitlebar.js +++ /dev/null @@ -1,14 +0,0 @@ -const customTitlebar = require("custom-electron-titlebar"); - -module.exports = () => { - const bar = new customTitlebar.Titlebar({ - backgroundColor: customTitlebar.Color.fromHex("#050505"), - minimizable: false, - maximizable: false, - menu: null - }); - let mainStyle = document.querySelector('#container').style; - mainStyle.width = '100%'; - mainStyle.position = 'fixed'; - mainStyle.border = 'unset'; -} \ No newline at end of file diff --git a/providers/prompt/darkPrompt.css b/providers/prompt/dark-prompt.css similarity index 100% rename from providers/prompt/darkPrompt.css rename to providers/prompt/dark-prompt.css diff --git a/providers/prompt/index.js b/providers/prompt/index.js index 0d89ebe9..302eeea0 100644 --- a/providers/prompt/index.js +++ b/providers/prompt/index.js @@ -1,9 +1,9 @@ -const electron = require('electron'); +const electron = require("electron"); const BrowserWindow = electron.BrowserWindow || electron.remote.BrowserWindow; const ipcMain = electron.ipcMain || electron.remote.ipcMain; -const url = require('url'); -const path = require('path'); +const url = require("url"); +const path = require("path"); const DEFAULT_WIDTH = 370; const DEFAULT_HEIGHT = 160; @@ -38,7 +38,7 @@ function electronPrompt(options, parentWindow) { options || {} ); - if (options_.type === 'select' && (options_.selectOptions === null || typeof options_.selectOptions !== 'object')) { + if (options_.type === "select" && (options_.selectOptions === null || typeof options_.selectOptions !== "object")) { reject(new Error('"selectOptions" must be an object')); return; } @@ -75,9 +75,9 @@ function electronPrompt(options, parentWindow) { }; const cleanup = () => { - ipcMain.removeListener('prompt-get-options:' + id, getOptionsListener); - ipcMain.removeListener('prompt-post-data:' + id, postDataListener); - ipcMain.removeListener('prompt-error:' + id, errorListener); + ipcMain.removeListener("prompt-get-options:" + id, getOptionsListener); + ipcMain.removeListener("prompt-post-data:" + id, postDataListener); + ipcMain.removeListener("prompt-error:" + id, errorListener); if (promptWindow) { promptWindow.close(); @@ -92,7 +92,7 @@ function electronPrompt(options, parentWindow) { }; const unresponsiveListener = () => { - reject(new Error('Window was unresponsive')); + reject(new Error("Window was unresponsive")); cleanup(); }; @@ -102,21 +102,21 @@ function electronPrompt(options, parentWindow) { cleanup(); }; - ipcMain.on('prompt-get-options:' + id, getOptionsListener); - ipcMain.on('prompt-post-data:' + id, postDataListener); - ipcMain.on('prompt-error:' + id, errorListener); - promptWindow.on('unresponsive', unresponsiveListener); + ipcMain.on("prompt-get-options:" + id, getOptionsListener); + ipcMain.on("prompt-post-data:" + id, postDataListener); + ipcMain.on("prompt-error:" + id, errorListener); + promptWindow.on("unresponsive", unresponsiveListener); - promptWindow.on('closed', () => { + promptWindow.on("closed", () => { promptWindow = null; cleanup(); resolve(null); }); const promptUrl = url.format({ - protocol: 'file', + protocol: "file", slashes: true, - pathname: path.join(__dirname, 'page', 'prompt.html'), + pathname: path.join(__dirname, "page", "prompt.html"), hash: id }); diff --git a/providers/prompt/page/prompt.js b/providers/prompt/page/prompt.js index 754d0e74..67acd31d 100644 --- a/providers/prompt/page/prompt.js +++ b/providers/prompt/page/prompt.js @@ -1,6 +1,6 @@ -const fs = require('fs'); -const {ipcRenderer} = require('electron'); -const docReady = require('doc-ready'); +const fs = require("fs"); +const {ipcRenderer} = require("electron"); +const docReady = require("doc-ready"); let promptId = null; let promptOptions = null; @@ -10,41 +10,39 @@ function promptError(error) { error = error.message; } - ipcRenderer.sendSync('prompt-error:' + promptId, error); + ipcRenderer.sendSync("prompt-error:" + promptId, error); } function promptCancel() { - ipcRenderer.sendSync('prompt-post-data:' + promptId, null); + ipcRenderer.sendSync("prompt-post-data:" + promptId, null); } function promptSubmit() { - const dataElement = document.querySelector('#data'); + const dataElement = document.querySelector("#data"); let data = null; - if (promptOptions.type === 'input') { + if (promptOptions.type === "input") { data = dataElement.value; - } else if (promptOptions.type === 'select') { - if (promptOptions.selectMultiple) { - data = dataElement.querySelectorAll('option[selected]').map(o => o.getAttribute('value')); - } else { - data = dataElement.value; - } + } else if (promptOptions.type === "select") { + data = promptOptions.selectMultiple ? + dataElement.querySelectorAll("option[selected]").map(o => o.getAttribute("value")) : + dataElement.value; } - ipcRenderer.sendSync('prompt-post-data:' + promptId, data); + ipcRenderer.sendSync("prompt-post-data:" + promptId, data); } function promptCreateInput() { - const dataElement = document.createElement('input'); - dataElement.setAttribute('type', 'text'); + const dataElement = document.createElement("input"); + dataElement.setAttribute("type", "text"); if (promptOptions.value) { dataElement.value = promptOptions.value; } else { - dataElement.value = ''; + dataElement.value = ""; } - if (promptOptions.inputAttrs && typeof (promptOptions.inputAttrs) === 'object') { + if (promptOptions.inputAttrs && typeof (promptOptions.inputAttrs) === "object") { for (const k in promptOptions.inputAttrs) { if (!Object.prototype.hasOwnProperty.call(promptOptions.inputAttrs, k)) { continue; @@ -54,16 +52,16 @@ function promptCreateInput() { } } - dataElement.addEventListener('keyup', event => { - if (event.key === 'Escape') { + dataElement.addEventListener("keyup", event => { + if (event.key === "Escape") { promptCancel(); } }); - dataElement.addEventListener('keypress', event => { - if (event.key === 'Enter') { + dataElement.addEventListener("keypress", event => { + if (event.key === "Enter") { event.preventDefault(); - document.querySelector('#ok').click(); + document.querySelector("#ok").click(); } }); @@ -71,7 +69,7 @@ function promptCreateInput() { } function promptCreateSelect() { - const dataElement = document.createElement('select'); + const dataElement = document.createElement("select"); let optionElement; for (const k in promptOptions.selectOptions) { @@ -79,11 +77,11 @@ function promptCreateSelect() { continue; } - optionElement = document.createElement('option'); - optionElement.setAttribute('value', k); + optionElement = document.createElement("option"); + optionElement.setAttribute("value", k); optionElement.textContent = promptOptions.selectOptions[k]; if (k === promptOptions.value) { - optionElement.setAttribute('selected', 'selected'); + optionElement.setAttribute("selected", "selected"); } dataElement.append(optionElement); @@ -93,34 +91,34 @@ function promptCreateSelect() { } function promptRegister() { - promptId = document.location.hash.replace('#', ''); + promptId = document.location.hash.replace("#", ""); try { - promptOptions = JSON.parse(ipcRenderer.sendSync('prompt-get-options:' + promptId)); + promptOptions = JSON.parse(ipcRenderer.sendSync("prompt-get-options:" + promptId)); } catch (error) { return promptError(error); } if (promptOptions.useHtmlLabel) { - document.querySelector('#label').innerHTML = promptOptions.label; + document.querySelector("#label").innerHTML = promptOptions.label; } else { - document.querySelector('#label').textContent = promptOptions.label; + document.querySelector("#label").textContent = promptOptions.label; } if (promptOptions.buttonLabels && promptOptions.buttonLabels.ok) { - document.querySelector('#ok').textContent = promptOptions.buttonLabels.ok; + document.querySelector("#ok").textContent = promptOptions.buttonLabels.ok; } if (promptOptions.buttonLabels && promptOptions.buttonLabels.cancel) { - document.querySelector('#cancel').textContent = promptOptions.buttonLabels.cancel; + document.querySelector("#cancel").textContent = promptOptions.buttonLabels.cancel; } if (promptOptions.customStylesheet) { try { const customStyleContent = fs.readFileSync(promptOptions.customStylesheet); if (customStyleContent) { - const customStyle = document.createElement('style'); - customStyle.setAttribute('rel', 'stylesheet'); + const customStyle = document.createElement("style"); + customStyle.setAttribute("rel", "stylesheet"); customStyle.append(document.createTextNode(customStyleContent)); document.head.append(customStyle); } @@ -129,25 +127,25 @@ function promptRegister() { } } - document.querySelector('#form').addEventListener('submit', promptSubmit); - document.querySelector('#cancel').addEventListener('click', promptCancel); + document.querySelector("#form").addEventListener("submit", promptSubmit); + document.querySelector("#cancel").addEventListener("click", promptCancel); - const dataContainerElement = document.querySelector('#data-container'); + const dataContainerElement = document.querySelector("#data-container"); let dataElement; - if (promptOptions.type === 'input') { + if (promptOptions.type === "input") { dataElement = promptCreateInput(); - } else if (promptOptions.type === 'select') { + } else if (promptOptions.type === "select") { dataElement = promptCreateSelect(); } else { return promptError(`Unhandled input type '${promptOptions.type}'`); } dataContainerElement.append(dataElement); - dataElement.setAttribute('id', 'data'); + dataElement.setAttribute("id", "data"); dataElement.focus(); - if (promptOptions.type === 'input') { + if (promptOptions.type === "input") { dataElement.select(); } @@ -161,10 +159,10 @@ function promptRegister() { } } -window.addEventListener('error', error => { +window.addEventListener("error", error => { if (promptId) { - promptError('An error has occured on the prompt window: \n' + error); + promptError("An error has occured on the prompt window: \n" + error); } }); -docReady(promptRegister); \ No newline at end of file +docReady(promptRegister); From 0e9b15722a02fc1e423fb0d5eec4cd969d397dae Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Tue, 6 Apr 2021 01:30:47 +0300 Subject: [PATCH 4/5] add inline doc --- index.js | 18 ++-- menu.js | 1 + providers/logger.js | 20 ++-- providers/prompt/index.js | 41 +++++--- providers/prompt/page/prompt.js | 168 ++++++++++++++++++-------------- providers/prompt/readme.md | 5 +- 6 files changed, 148 insertions(+), 105 deletions(-) diff --git a/index.js b/index.js index 9e8b7233..7fd88754 100644 --- a/index.js +++ b/index.js @@ -110,8 +110,8 @@ function createMainWindow() { titleBarStyle: useInlineMenu ? "hidden" : is.macOS() - ? "hiddenInset" - : "default", + ? "hiddenInset" + : "default", autoHideMenuBar: config.get("options.hideMenu"), }); if (windowPosition) { @@ -156,7 +156,7 @@ function createMainWindow() { let createdWindow = false; app.on("browser-window-created", (event, win) => { - //Ensure listeners aren't registered when creating input dialog + //Ensures listeners are registered only once if (createdWindow) { return; } @@ -172,7 +172,7 @@ app.on("browser-window-created", (event, win) => { frameProcessId, frameRoutingId, ) => { - let log = { + const log = { error: "did-fail-load", event, errorCode, @@ -183,7 +183,7 @@ app.on("browser-window-created", (event, win) => { frameRoutingId, }; if (is.dev()) { - console.log(log); + console.log(log.toString()); } win.webContents.send("log", log); win.webContents.loadFile(path.join(__dirname, "error.html")); @@ -306,13 +306,11 @@ app.on("ready", () => { } // Optimized for Mac OS X - if (is.macOS()) { - if (!config.get("options.appVisible")) { - app.dock.hide(); - } + if (is.macOS() && !config.get("options.appVisible")) { + app.dock.hide(); } - var forceQuit = false; + let forceQuit = false; app.on("before-quit", () => { forceQuit = true; }); diff --git a/menu.js b/menu.js index c0495d68..21ac1e7a 100644 --- a/menu.js +++ b/menu.js @@ -12,6 +12,7 @@ const pluginEnabledMenu = (win, plugin, label = "", hasSubmenu = false) => ({ label: label || plugin, type: "checkbox", checked: config.plugins.isEnabled(plugin), + //Submenu check used in in-app-menu hasSubmenu: hasSubmenu || undefined, click: (item) => { if (item.checked) { diff --git a/providers/logger.js b/providers/logger.js index 08a940f3..730a3dfa 100644 --- a/providers/logger.js +++ b/providers/logger.js @@ -1,11 +1,15 @@ const { ipcRenderer } = require("electron"); +function logToString(log) { + let string = (typeof log === "string") ? log : log.toString(); + if (!string || string.includes("[object Object]")) { + string = JSON.stringify(log); + } + return string; +} + module.exports = () => { - ipcRenderer.on("log", (event, log) => { - let string = log || log.toString(); - if (!string || string === "[object Object]") { - string = JSON.stringify(log); - } - console.log(string); - }) -}; \ No newline at end of file + ipcRenderer.on("log", (event, log) => { + console.log(logToString(log)); + }); +}; diff --git a/providers/prompt/index.js b/providers/prompt/index.js index 302eeea0..bbba8b78 100644 --- a/providers/prompt/index.js +++ b/providers/prompt/index.js @@ -10,8 +10,10 @@ const DEFAULT_HEIGHT = 160; function electronPrompt(options, parentWindow) { return new Promise((resolve, reject) => { + //id used to ensure unique listeners per window const id = `${Date.now()}-${Math.random()}`; + //custom options override default const options_ = Object.assign( { width: DEFAULT_WIDTH, @@ -19,12 +21,12 @@ function electronPrompt(options, parentWindow) { minWidth: DEFAULT_WIDTH, minHeight: DEFAULT_HEIGHT, resizable: false, - title: 'Prompt', - label: 'Please input a value:', + title: "Prompt", + label: "Please input a value:", buttonLabels: null, alwaysOnTop: false, value: null, - type: 'input', + type: "input", selectOptions: null, icon: null, useHtmlLabel: false, @@ -70,10 +72,7 @@ function electronPrompt(options, parentWindow) { promptWindow.setMenu(null); promptWindow.setMenuBarVisibility(options_.menuBarVisible); - const getOptionsListener = event => { - event.returnValue = JSON.stringify(options_); - }; - + //called on exit const cleanup = () => { ipcMain.removeListener("prompt-get-options:" + id, getOptionsListener); ipcMain.removeListener("prompt-post-data:" + id, postDataListener); @@ -85,6 +84,12 @@ function electronPrompt(options, parentWindow) { } }; + ///transfer options to front + const getOptionsListener = event => { + event.returnValue = JSON.stringify(options_); + }; + + //get input from front const postDataListener = (event, value) => { resolve(value); event.returnValue = null; @@ -96,12 +101,14 @@ function electronPrompt(options, parentWindow) { cleanup(); }; + //get error from front const errorListener = (event, message) => { reject(new Error(message)); event.returnValue = null; cleanup(); }; + //attach listeners ipcMain.on("prompt-get-options:" + id, getOptionsListener); ipcMain.on("prompt-post-data:" + id, postDataListener); ipcMain.on("prompt-error:" + id, errorListener); @@ -113,13 +120,23 @@ function electronPrompt(options, parentWindow) { resolve(null); }); - const promptUrl = url.format({ - protocol: "file", - slashes: true, - pathname: path.join(__dirname, "page", "prompt.html"), - hash: id + //should never happen + promptWindow.webContents.on("did-fail-load", ( + event, + errorCode, + errorDescription, + validatedURL, + ) => { + const log = { + error: "did-fail-load", + errorCode, + errorDescription, + validatedURL, + }; + reject(new Error("prompt.html did-fail-load, log:\n", + log.toString())); }); + //Finally, load prompt promptWindow.loadURL(promptUrl); }); } diff --git a/providers/prompt/page/prompt.js b/providers/prompt/page/prompt.js index 67acd31d..b7a54120 100644 --- a/providers/prompt/page/prompt.js +++ b/providers/prompt/page/prompt.js @@ -5,6 +5,93 @@ const docReady = require("doc-ready"); let promptId = null; let promptOptions = null; + +docReady(promptRegister); +//start here +function promptRegister() { + //get custom session id + promptId = document.location.hash.replace("#", ""); + + //get options from back + try { + promptOptions = JSON.parse(ipcRenderer.sendSync("prompt-get-options:" + promptId)); + } catch (error) { + return promptError(error); + } + + //set label + if (promptOptions.useHtmlLabel) { + document.querySelector("#label").innerHTML = promptOptions.label; + } else { + document.querySelector("#label").textContent = promptOptions.label; + } + + //set button label + if (promptOptions.buttonLabels && promptOptions.buttonLabels.ok) { + document.querySelector("#ok").textContent = promptOptions.buttonLabels.ok; + } + + if (promptOptions.buttonLabels && promptOptions.buttonLabels.cancel) { + document.querySelector("#cancel").textContent = promptOptions.buttonLabels.cancel; + } + + //inject custom stylesheet from options + if (promptOptions.customStylesheet) { + try { + const customStyleContent = fs.readFileSync(promptOptions.customStylesheet); + if (customStyleContent) { + const customStyle = document.createElement("style"); + customStyle.setAttribute("rel", "stylesheet"); + customStyle.append(document.createTextNode(customStyleContent)); + document.head.append(customStyle); + } + } catch (error) { + return promptError(error); + } + } + + //add button listeners + document.querySelector("#form").addEventListener("submit", promptSubmit); + document.querySelector("#cancel").addEventListener("click", promptCancel); + + + //create input/select + const dataContainerElement = document.querySelector("#data-container"); + let dataElement; + if (promptOptions.type === "input") { + dataElement = promptCreateInput(); + } else if (promptOptions.type === "select") { + dataElement = promptCreateSelect(); + } else { + return promptError(`Unhandled input type '${promptOptions.type}'`); + } + + dataContainerElement.append(dataElement); + dataElement.setAttribute("id", "data"); + + dataElement.focus(); + if (promptOptions.type === "input") { + dataElement.select(); + } + + //load custom script from options + if (promptOptions.customScript) { + try { + const customScript = require(promptOptions.customScript); + customScript(); + } catch (error) { + return promptError(error); + } + } +} + +window.addEventListener("error", error => { + if (promptId) { + promptError("An error has occured on the prompt window: \n" + error); + } +}); + +//send error to back function promptError(error) { if (error instanceof Error) { error = error.message; @@ -13,10 +100,12 @@ function promptError(error) { ipcRenderer.sendSync("prompt-error:" + promptId, error); } +//send to back: input=null function promptCancel() { ipcRenderer.sendSync("prompt-post-data:" + promptId, null); } +//transfer input data to back function promptSubmit() { const dataElement = document.querySelector("#data"); let data = null; @@ -32,6 +121,7 @@ function promptSubmit() { ipcRenderer.sendSync("prompt-post-data:" + promptId, data); } +//creates input box function promptCreateInput() { const dataElement = document.createElement("input"); dataElement.setAttribute("type", "text"); @@ -42,6 +132,7 @@ function promptCreateInput() { dataElement.value = ""; } + //insert custom input attributes if in options if (promptOptions.inputAttrs && typeof (promptOptions.inputAttrs) === "object") { for (const k in promptOptions.inputAttrs) { if (!Object.prototype.hasOwnProperty.call(promptOptions.inputAttrs, k)) { @@ -52,12 +143,14 @@ function promptCreateInput() { } } + //Cancel/Exit on 'Escape' dataElement.addEventListener("keyup", event => { if (event.key === "Escape") { promptCancel(); } }); + //Confrim on 'Enter' dataElement.addEventListener("keypress", event => { if (event.key === "Enter") { event.preventDefault(); @@ -68,6 +161,7 @@ function promptCreateInput() { return dataElement; } +//create multiple select function promptCreateSelect() { const dataElement = document.createElement("select"); let optionElement; @@ -90,79 +184,5 @@ function promptCreateSelect() { return dataElement; } -function promptRegister() { - promptId = document.location.hash.replace("#", ""); - try { - promptOptions = JSON.parse(ipcRenderer.sendSync("prompt-get-options:" + promptId)); - } catch (error) { - return promptError(error); - } - if (promptOptions.useHtmlLabel) { - document.querySelector("#label").innerHTML = promptOptions.label; - } else { - document.querySelector("#label").textContent = promptOptions.label; - } - - if (promptOptions.buttonLabels && promptOptions.buttonLabels.ok) { - document.querySelector("#ok").textContent = promptOptions.buttonLabels.ok; - } - - if (promptOptions.buttonLabels && promptOptions.buttonLabels.cancel) { - document.querySelector("#cancel").textContent = promptOptions.buttonLabels.cancel; - } - - if (promptOptions.customStylesheet) { - try { - const customStyleContent = fs.readFileSync(promptOptions.customStylesheet); - if (customStyleContent) { - const customStyle = document.createElement("style"); - customStyle.setAttribute("rel", "stylesheet"); - customStyle.append(document.createTextNode(customStyleContent)); - document.head.append(customStyle); - } - } catch (error) { - return promptError(error); - } - } - - document.querySelector("#form").addEventListener("submit", promptSubmit); - document.querySelector("#cancel").addEventListener("click", promptCancel); - - const dataContainerElement = document.querySelector("#data-container"); - - let dataElement; - if (promptOptions.type === "input") { - dataElement = promptCreateInput(); - } else if (promptOptions.type === "select") { - dataElement = promptCreateSelect(); - } else { - return promptError(`Unhandled input type '${promptOptions.type}'`); - } - - dataContainerElement.append(dataElement); - dataElement.setAttribute("id", "data"); - - dataElement.focus(); - if (promptOptions.type === "input") { - dataElement.select(); - } - - if (promptOptions.customScript) { - try { - const customScript = require(promptOptions.customScript); - customScript(); - } catch (error) { - return promptError(error); - } - } -} - -window.addEventListener("error", error => { - if (promptId) { - promptError("An error has occured on the prompt window: \n" + error); - } -}); - -docReady(promptRegister); diff --git a/providers/prompt/readme.md b/providers/prompt/readme.md index 2eb13b74..6683aced 100644 --- a/providers/prompt/readme.md +++ b/providers/prompt/readme.md @@ -3,11 +3,14 @@

prompt-preview

## Usage - ```js prompt([options, parentBrowserWindow]).then(...).catch(...) ``` +Promise resolve returns input +If user presses cancel/exit window, input = null; + +On error, Prompise reject returns custom error message ## Example ```js From 17fd499420486cdb4d77ec5d8387df8f692bcbbe Mon Sep 17 00:00:00 2001 From: Araxeus <78568641+Araxeus@users.noreply.github.com> Date: Tue, 6 Apr 2021 01:51:11 +0300 Subject: [PATCH 5/5] fix typo --- menu.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/menu.js b/menu.js index 21ac1e7a..90487970 100644 --- a/menu.js +++ b/menu.js @@ -338,10 +338,10 @@ function setProxy(item, win) { .then((input) => { if (input !== null && input !== example) { config.set("options.proxy", input); - item.checked = (input === "") ? false : true; + item.checked = input !== ""; } else { //user pressed cancel item.checked = !item.checked; //reset checkbox } }) .catch(console.error); -} \ No newline at end of file +}