feat: add unified contest pool and realtime game modes

This commit is contained in:
2026-08-10 00:31:21 +08:00
parent 97ca20413e
commit 40e9462842
18 changed files with 822 additions and 152 deletions
+21 -9
View File
@@ -29,7 +29,7 @@ const state = {
user: null,
stories: [],
contests: [],
track: "standard",
matchMode: "quiz",
videoCatalog: null,
videos: [],
videoAbility: "",
@@ -446,12 +446,21 @@ async function loadContests() {
function renderContests() {
const root = $("#contest-list");
const contests = state.contests.filter((contest) => contest.track === state.track);
const contests = ["realtime", "daily", "practice"]
.map((kind) => {
const candidates = state.contests.filter((contest) => contest.kind === kind);
return (
candidates.find((contest) => contest.track === "open") ||
candidates.find((contest) => contest.track === "standard") ||
candidates[0]
);
})
.filter(Boolean);
root.classList.remove("loading");
root.replaceChildren(...contests.map((contest) => card({
meta: contest.kind === "realtime" ? "实时竞技" : contest.kind === "daily" ? "今日挑战" : "自由练习",
title: contest.title,
body: contest.kind === "realtime" ? "Rating 匹配,同题序列,服务端计时和唯一结算。" : "完成整组题目,正式答案只在提交后显示。",
title: contest.title.replace(/^(入门|标准|进阶)/, ""),
body: contest.kind === "realtime" ? "统一玩家池,支持口算、数独与 24 点,服务端计时和 Rating 结算。" : "每次随机抽取题目,正式答案只在提交后显示。",
foot: `${contest.duration_seconds}`,
action: contest.kind === "realtime" ? "开始匹配 →" : "开始答题 →",
onClick: () => beginContest(contest),
@@ -827,7 +836,7 @@ async function loadProfile() {
profile.recent_games.forEach((game) => {
const item = document.createElement("div");
const name = document.createElement("b");
name.textContent = `${game.label} · ${game.difficulty}`;
name.textContent = game.label;
const detail = document.createElement("span");
detail.textContent =
game.status === "completed"
@@ -1255,10 +1264,13 @@ function bindUI() {
$("#register-form").classList.toggle("hidden", button.dataset.authTab !== "register");
$("#auth-error").textContent = "";
}));
$$("#track-switch button").forEach((button) => button.addEventListener("click", () => {
state.track = button.dataset.track;
$$("#track-switch button").forEach((item) => item.classList.toggle("active", item === button));
renderContests();
$$("#match-mode-switch button").forEach((button) => button.addEventListener("click", () => {
state.matchMode = button.dataset.matchMode;
$$("#match-mode-switch button").forEach((item) => {
item.classList.toggle("active", item === button);
});
const label = button.querySelector("b").textContent;
$("#challenge-create").textContent = `创建${label}约战`;
}));
$("#login-form").addEventListener("submit", async (event) => {
event.preventDefault();