diff --git a/index.css b/index.css index d129d5a5..6d121555 100644 --- a/index.css +++ b/index.css @@ -2273,8 +2273,9 @@ svg.button { .dontAsk { - display: inline-block; margin: 0.9em 0 0 0.6em; + display: inline-flex; + align-items: center; } #errorBox { diff --git a/index.html b/index.html index d68f815e..7fcc6759 100644 --- a/index.html +++ b/index.html @@ -6297,7 +6297,7 @@ - + diff --git a/main.js b/main.js index d565e4da..a677d7ad 100644 --- a/main.js +++ b/main.js @@ -10,13 +10,22 @@ const TIME = DEBUG || !PRODUCTION; const WARN = true; const ERROR = true; -// register service worker responsible for caching if ("serviceWorker" in navigator) { window.addEventListener("load", () => { navigator.serviceWorker.register("./sw.js").catch(err => { console.error("ServiceWorker registration failed: ", err); }); }); + + window.addEventListener( + "beforeinstallprompt", + async event => { + event.preventDefault(); + const Installation = await import("/modules/dynamic/installation.js"); + Installation.init(event); + }, + {once: true} + ); } // append svg layers (in default order) diff --git a/modules/dynamic/installation.js b/modules/dynamic/installation.js new file mode 100644 index 00000000..c6c23ee5 --- /dev/null +++ b/modules/dynamic/installation.js @@ -0,0 +1,70 @@ +// module to prompt PWA installation +let installButton = null; +let deferredPrompt = null; + +export function init(event) { + const dontAskforInstallation = localStorage.getItem("installationDontAsk"); + if (dontAskforInstallation) return; + + installButton = createButton(); + deferredPrompt = event; + + window.addEventListener("appinstalled", () => { + tip("Application is installed", false, "success", 8000); + cleanup(); + }); +} + +function createButton() { + const button = document.createElement("button"); + button.style = ` + position: fixed; + top: 1em; + right: 1em; + padding: 0.6em 0.8em; + width: auto; + `; + button.className = "options glow"; + button.innerHTML = "Install"; + button.onclick = openDialog; + button.onmouseenter = () => tip("Install the Application"); + document.querySelector("body").appendChild(button); + return button; +} + +function openDialog() { + alertMessage.innerHTML = /* html */ `You can install the tool so that it will look and feel like desktop application`; + $("#alert").dialog({ + resizable: false, + title: "Install the Application", + buttons: { + Install: function () { + $(this).dialog("close"); + deferredPrompt.prompt(); + }, + Cancel: function () { + $(this).dialog("close"); + } + }, + open: function () { + const checkbox = + ''; + const pane = this.parentElement.querySelector(".ui-dialog-buttonpane"); + pane.insertAdjacentHTML("afterbegin", checkbox); + }, + close: function () { + const box = this.parentElement.querySelector(".checkbox"); + if (box?.checked) { + localStorage.setItem("installationDontAsk", true); + cleanup(); + } + $(this).dialog("destroy"); + } + }); + + function cleanup() { + installButton.remove(); + installButton = null; + deferredPrompt = null; + } +} diff --git a/modules/ui/tools.js b/modules/ui/tools.js index 2189b1d8..66eaa9ea 100644 --- a/modules/ui/tools.js +++ b/modules/ui/tools.js @@ -2,10 +2,7 @@ // module to control the Tools options (click to edit, to re-geenerate, tp add) toolsContent.addEventListener("click", function (event) { - if (customization) { - tip("Please exit the customization mode first", false, "warning"); - return; - } + if (customization) return tip("Please exit the customization mode first", false, "warning"); if (!["BUTTON", "I"].includes(event.target.tagName)) return; const button = event.target.id;