initial notification checkbox
This commit is contained in:
parent
6ba7d08e97
commit
3f9ee57208
1 changed files with 42 additions and 4 deletions
46
index.html
46
index.html
|
|
@ -410,6 +410,8 @@
|
||||||
solution</button>
|
solution</button>
|
||||||
<input id="importFile" type="file" accept="application/json,.json" style="display:none"
|
<input id="importFile" type="file" accept="application/json,.json" style="display:none"
|
||||||
onchange="onImportFile(event)">
|
onchange="onImportFile(event)">
|
||||||
|
<label id="notifyWrap" style="display:none;margin-left:.6rem">
|
||||||
|
<input id="notifyDone" type="checkbox"> Notify when queued items complete</label>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div id="error" class="err"></div>
|
<div id="error" class="err"></div>
|
||||||
|
|
@ -1006,6 +1008,34 @@
|
||||||
// queued -> running -> done (rendered) / error / cancelled.
|
// queued -> running -> done (rendered) / error / cancelled.
|
||||||
const pending = new Map();
|
const pending = new Map();
|
||||||
|
|
||||||
|
// --- "notify when queue empty" checkbox ---
|
||||||
|
// The checkbox only appears while solves are queued/running. Permission is
|
||||||
|
// requested the first time it's checked (not on page load). When the queue
|
||||||
|
// drains to empty it fires a notification (if still checked), then unchecks
|
||||||
|
// itself and hides until there's work again.
|
||||||
|
const notifyWrap = document.getElementById("notifyWrap");
|
||||||
|
const notifyDone = document.getElementById("notifyDone");
|
||||||
|
notifyDone.onchange = () => {
|
||||||
|
if (notifyDone.checked && "Notification" in window
|
||||||
|
&& Notification.permission === "default") {
|
||||||
|
Notification.requestPermission();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Show/hide the checkbox to match the current queue state, and uncheck it
|
||||||
|
// once the queue is empty. Call after any change to `pending`.
|
||||||
|
function updateNotifyUi() {
|
||||||
|
notifyWrap.style.display = pending.size ? "" : "none";
|
||||||
|
if (!pending.size) notifyDone.checked = false;
|
||||||
|
}
|
||||||
|
// Fire the "queue empty" notification if the user opted in and granted it.
|
||||||
|
function notifyQueueEmpty() {
|
||||||
|
if (notifyDone.checked && "Notification" in window
|
||||||
|
&& Notification.permission === "granted") {
|
||||||
|
new Notification("DWS Solver",
|
||||||
|
{body: "All queued solves have finished."});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function uuid() {
|
function uuid() {
|
||||||
return crypto.randomUUID ? crypto.randomUUID()
|
return crypto.randomUUID ? crypto.randomUUID()
|
||||||
: String(Date.now()) + Math.random();
|
: String(Date.now()) + Math.random();
|
||||||
|
|
@ -1168,7 +1198,7 @@
|
||||||
card.append(
|
card.append(
|
||||||
el("div", {class: "pending-head"},
|
el("div", {class: "pending-head"},
|
||||||
[el("span", {class: "spinner"}), nameSpan,
|
[el("span", {class: "spinner"}), nameSpan,
|
||||||
document.createTextNode(" — "), status]),
|
document.createTextNode(" — "), status]),
|
||||||
actions);
|
actions);
|
||||||
return card;
|
return card;
|
||||||
}
|
}
|
||||||
|
|
@ -1190,7 +1220,11 @@
|
||||||
if (!entry) return;
|
if (!entry) return;
|
||||||
pending.delete(token);
|
pending.delete(token);
|
||||||
action(entry);
|
action(entry);
|
||||||
if (pending.size === 0 && stream) {stream.close(); stream = null;}
|
if (pending.size === 0) {
|
||||||
|
if (stream) {stream.close(); stream = null;}
|
||||||
|
notifyQueueEmpty();
|
||||||
|
}
|
||||||
|
updateNotifyUi();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Put a pending card into a terminal state: show the message, drop the
|
// Put a pending card into a terminal state: show the message, drop the
|
||||||
|
|
@ -1283,6 +1317,7 @@
|
||||||
// Stash the inputs alongside the pending solve so, once it finishes,
|
// Stash the inputs alongside the pending solve so, once it finishes,
|
||||||
// its card can export a self-contained {problem, solution} bundle.
|
// its card can export a self-contained {problem, solution} bundle.
|
||||||
pending.set(token, {token, card, n, confirmed: false, problem, maxTime: time});
|
pending.set(token, {token, card, n, confirmed: false, problem, maxTime: time});
|
||||||
|
updateNotifyUi();
|
||||||
fetch("/solve", {
|
fetch("/solve", {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {"Content-Type": "application/json"},
|
headers: {"Content-Type": "application/json"},
|
||||||
|
|
@ -1330,8 +1365,10 @@
|
||||||
}
|
}
|
||||||
actions.append(el("button", {
|
actions.append(el("button", {
|
||||||
class: "mini", type: "button",
|
class: "mini", type: "button",
|
||||||
onclick: () => exportSolve({token, n, solution: s, problem, maxTime,
|
onclick: () => exportSolve({
|
||||||
name: solveLabel(token, n)}),
|
token, n, solution: s, problem, maxTime,
|
||||||
|
name: solveLabel(token, n)
|
||||||
|
}),
|
||||||
}, "Export"));
|
}, "Export"));
|
||||||
// The problem may be on hand (a solve queued from this tab) or, for a
|
// The problem may be on hand (a solve queued from this tab) or, for a
|
||||||
// solve opened via a share link, fetched from the server by token on
|
// solve opened via a share link, fetched from the server by token on
|
||||||
|
|
@ -1742,6 +1779,7 @@
|
||||||
// Load in reverse so the first id ends up on top (each load prepends).
|
// Load in reverse so the first id ends up on top (each load prepends).
|
||||||
for (const token of sharedTokens.slice().reverse()) loadSharedSolve(token);
|
for (const token of sharedTokens.slice().reverse()) loadSharedSolve(token);
|
||||||
if (sharedTokens.length) syncStream();
|
if (sharedTokens.length) syncStream();
|
||||||
|
updateNotifyUi();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue