// 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.title")}

`; 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("kind." + card.doc)} ${esc(date.day)} ${esc(date.month)} ${esc(date.year)}
${esc(from.seal)} ${esc(tx(from.name))}${esc(tx(from.role))}

${esc(tx(card.title))}

${paragraphs}
${option(card, "yes")}${option(card, "no")}

${t("card.keys")}

`; } // The tab counts the proposals waiting; the pile under the top paper grows with them. function updateTray(state) { const n = state.inbox.length; countEl.textContent = n ? `${n}/${inboxMax}` : ""; countEl.classList.toggle("is-warn", n >= inboxMax - 2); if (n > lastCount) { countEl.classList.remove("is-bump"); void countEl.offsetWidth; // restart the animation countEl.classList.add("is-bump"); } lastCount = n; fullEl.hidden = !(state.pending.inboxFull && !state.over); const card = $(body, ".card"); if (card) card.dataset.pile = Math.min(3, Math.max(0, n - 1)); } function update(state) { latest = state; updateTray(state); let kind = "idle"; if (state.over) kind = "over"; else if (state.pending.profile) kind = "profile"; else if (state.inbox.length) kind = `card:${state.inbox[0].id}`; if (kind === shown) return; shown = kind; locked = false; timers.forEach(clearTimeout); timers = []; body.classList.toggle("is-card", kind.startsWith("card:")); if (kind === "profile") { body.innerHTML = `

${t("profile.title")}

${t("profile.lead")}

${t("profile.body")}

${["he", "she", "they"].map((p) => ``).join("")}

${t("profile.note")}

`; } else if (kind === "idle") { body.innerHTML = `

${t("inbox.idle")}

`; } else if (kind === "over") { body.innerHTML = `

${t("inbox.overTitle")}

${t("inbox.overBody")}

`; } else { body.innerHTML = cardHtml(cardsById.get(state.inbox[0].id), state); } } return { update, destroy: () => { timers.forEach(clearTimeout); document.removeEventListener("keydown", onKey); }, }; }