initial notification checkbox

This commit is contained in:
Pagwin 2026-07-01 20:01:27 -04:00
parent 6ba7d08e97
commit 3f9ee57208

View file

@ -410,6 +410,8 @@
solution</button>
<input id="importFile" type="file" accept="application/json,.json" style="display:none"
onchange="onImportFile(event)">
<label id="notifyWrap" style="display:none;margin-left:.6rem">
<input id="notifyDone" type="checkbox"> Notify when queued items complete</label>
</p>
<div id="error" class="err"></div>
@ -1006,6 +1008,34 @@
// queued -> running -> done (rendered) / error / cancelled.
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() {
return crypto.randomUUID ? crypto.randomUUID()
: String(Date.now()) + Math.random();
@ -1168,7 +1198,7 @@
card.append(
el("div", {class: "pending-head"},
[el("span", {class: "spinner"}), nameSpan,
document.createTextNode(" — "), status]),
document.createTextNode(" — "), status]),
actions);
return card;
}
@ -1190,7 +1220,11 @@
if (!entry) return;
pending.delete(token);
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
@ -1283,6 +1317,7 @@
// Stash the inputs alongside the pending solve so, once it finishes,
// its card can export a self-contained {problem, solution} bundle.
pending.set(token, {token, card, n, confirmed: false, problem, maxTime: time});
updateNotifyUi();
fetch("/solve", {
method: "POST",
headers: {"Content-Type": "application/json"},
@ -1330,8 +1365,10 @@
}
actions.append(el("button", {
class: "mini", type: "button",
onclick: () => exportSolve({token, n, solution: s, problem, maxTime,
name: solveLabel(token, n)}),
onclick: () => exportSolve({
token, n, solution: s, problem, maxTime,
name: solveLabel(token, n)
}),
}, "Export"));
// 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
@ -1742,6 +1779,7 @@
// Load in reverse so the first id ends up on top (each load prepends).
for (const token of sharedTokens.slice().reverse()) loadSharedSolve(token);
if (sharedTokens.length) syncStream();
updateNotifyUi();
</script>
</body>