${esc(tx(card.title))}
${t("card.keys")}
// Centre folder: the in-tray. The oldest waiting proposal is the document on top, answered with // two rubber stamps; the others show as a pile of sheets under it. Also shows the idle and // end-of-term messages. import { t, tx, dateParts } from "../i18n/i18n.js"; import { dateOfDay } from "../engine/index.js"; import { $, esc } from "./desk.js"; const STAMP_MS = 600; // how long the stamp animation plays before the paper leaves the tray const LEAVE_MS = 320; // and how long the paper takes to slide away export function createDecision(root, { cardsById, characters, inboxMax = 10, onAnswer, onStamp = () => {}, onProfile, keysEnabled = () => true }) { root.innerHTML = `
${t("inbox.full")}
`; const body = $(root, ".inbox__body"); const countEl = $(root, "[data-count]"); const fullEl = $(root, ".inbox__full"); let shown = ""; let locked = false; let latest = null; let lastCount = 0; let timers = []; function choose(choice) { if (locked) return; const card = $(body, ".card"); const id = latest?.inbox[0]?.id; if (!card || !id) return; locked = true; card.dataset.stamped = choice; for (const b of card.querySelectorAll("button")) b.disabled = true; const stamp = document.createElement("div"); stamp.className = `card__stamp card__stamp--${choice}`; stamp.textContent = t(choice === "yes" ? "card.approved" : "card.rejected"); card.append(stamp); onStamp(choice); timers.push(setTimeout(() => card.classList.add("card--leave"), STAMP_MS)); timers.push(setTimeout(() => onAnswer(choice, id), STAMP_MS + LEAVE_MS)); } body.addEventListener("click", (e) => { const b = e.target.closest("button[data-choice]"); if (b) choose(b.dataset.choice); const p = e.target.closest("button[data-pronouns]"); if (p) onProfile(p.dataset.pronouns); }); // The panel is rebuilt on language changes and new games, so its key listener must be removable. const onKey = (e) => { if (!keysEnabled() || !latest?.inbox.length) return; if (latest.pending.review || latest.pending.profile || latest.pending.alert) return; if (e.ctrlKey || e.metaKey || e.altKey) return; if (e.target.closest?.("input, textarea, select")) return; const k = e.key.toLowerCase(); if (k === "y") choose("yes"); else if (k === "n") choose("no"); }; document.addEventListener("keydown", onKey); function option(card, side) { const label = side === "yes" ? t("card.approved") : t("card.rejected"); return ` `; } function cardHtml(card, state) { const from = characters[card.sender]; const date = dateParts(dateOfDay(state.startDate, state.inbox[0].day).iso); const paragraphs = tx(card.body).split("\n\n").map((p) => `${esc(p)}
`).join(""); return `${t("card.keys")}
${t("profile.lead")}
${t("profile.body")}
${t("profile.note")}
`; } else if (kind === "idle") { body.innerHTML = `${t("inbox.idle")}
`; } else if (kind === "over") { body.innerHTML = `${t("inbox.overBody")}
`; } else { body.innerHTML = cardHtml(cardsById.get(state.inbox[0].id), state); } } return { update, destroy: () => { timers.forEach(clearTimeout); document.removeEventListener("keydown", onKey); }, }; }