Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 1x 1x 1x 2x 2x 2x 1x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 15x 14x 14x 11x 15x 9x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 15x 15x 5x 5x 5x 5x 3x 3x | // The way into a shared session from the More menu: a dialog to host on the machine as it is, or to join
// someone else's room, and once a session is going, its pane.
import * as bootstrap from "bootstrap";
import { roomFrom } from "./shared-session.js";
import { noteFeature } from "./analytics.js";
// An Enter held on Start hosting repeats after the dialog has gone, and the machine, which never saw it go down,
// would take each repeat as a fresh RETURN.
function holdBackEnterRepeats() {
const done = new AbortController();
const options = { capture: true, signal: done.signal };
window.addEventListener(
"keydown",
(event) => {
Iif (event.key !== "Enter" || !event.repeat) return;
event.preventDefault();
event.stopImmediatePropagation();
},
options,
);
window.addEventListener("keyup", (event) => event.key === "Enter" && done.abort(), options);
}
export class SessionDialog {
/**
* @param {object} options
* @param {{panel: import("./session-panel.js").SessionPanel}|null} options.session the page's session, if any
* @param {string|null} options.refusal why this machine cannot take part in a session, if it cannot
* @param {function(string): Promise<object|null>} options.host hosts as the given name, resolving to the session,
* or null if there is none
* @param {function(string, string): void} options.join joins the given room as the given name
*/
constructor(
{ session, refusal, host, join },
opener = document.getElementById("session-open"),
dialog = document.getElementById("session-dialog"),
) {
this.session = session;
const nameInput = dialog.querySelector(".session-name");
const roomInput = dialog.querySelector(".session-room");
const hostButton = dialog.querySelector(".session-host");
const joinButton = dialog.querySelector(".session-join");
const refusalText = dialog.querySelector(".session-refusal");
refusalText.textContent = refusal ?? "";
refusalText.hidden = !refusal;
hostButton.disabled = joinButton.disabled = !!refusal;
this.hosting = false;
// Bootstrap ignores a hide while the dialog is still fading in, which would leave hosting waiting for
// whatever closed it later.
let shown = false;
dialog.addEventListener("shown.bs.modal", () => (shown = true));
dialog.addEventListener("hide.bs.modal", () => (shown = false));
opener.addEventListener("click", (event) => {
event.preventDefault();
if (this.session) this.session.panel.open();
else if (!this.hosting) bootstrap.Modal.getOrCreateInstance(dialog).show();
});
// Hosting waits for the dialog to go, and for every other listener to its going (the page's hold on the
// machine among them) to have run, so the machine is no longer held for it.
hostButton.addEventListener("click", (event) => {
if (!shown) return;
noteFeature(dialog, "host");
this.hosting = true;
hostButton.disabled = joinButton.disabled = true;
if (event.detail === 0) holdBackEnterRepeats();
dialog.addEventListener(
"hidden.bs.modal",
async () => {
await new Promise((resolve) => setTimeout(resolve));
this.session = await host(nameInput.value);
this.hosting = false;
if (!this.session) hostButton.disabled = joinButton.disabled = false;
},
{ once: true },
);
bootstrap.Modal.getOrCreateInstance(dialog).hide();
});
roomInput.addEventListener("input", () => roomInput.classList.remove("is-invalid"));
dialog.querySelector(".session-join-form").addEventListener("submit", (event) => {
event.preventDefault();
const room = roomFrom(roomInput.value);
roomInput.classList.toggle("is-invalid", !room);
if (!room) return;
noteFeature(dialog, "join");
join(room, nameInput.value.trim());
});
}
}
|