Refresh vote cards after suggestion edits

This commit is contained in:
2026-02-02 23:33:52 +01:00
parent 14c166d143
commit 2add818045
2 changed files with 44 additions and 6 deletions

View File

@@ -12,6 +12,7 @@ const state = {
counts: null, counts: null,
mySuggestions: [], mySuggestions: [],
allSuggestions: [], allSuggestions: [],
allSuggestionsSig: null,
myVotes: [], myVotes: [],
results: [], results: [],
votesRendered: false votesRendered: false
@@ -114,9 +115,16 @@ async function loadSuggestData() {
async function loadRevealData() { async function loadRevealData() {
if (state.phase === "Reveal" || state.phase === "Vote" || state.phase === "Results") { if (state.phase === "Reveal" || state.phase === "Vote" || state.phase === "Results") {
state.allSuggestions = await api.allSuggestions(); const latest = await api.allSuggestions();
const latestSig = signatureSuggestions(latest);
const changed = latestSig !== state.allSuggestionsSig;
state.allSuggestions = latest;
state.allSuggestionsSig = latestSig;
renderAllSuggestions(); renderAllSuggestions();
renderPhaseTitles(); renderPhaseTitles();
if (state.phase === "Vote" && changed) {
state.votesRendered = false;
}
} }
} }
@@ -199,12 +207,14 @@ function renderVotes() {
const li = buildCard(s, { showAuthor: true, allowEdit: !!state.me?.isAdmin }); const li = buildCard(s, { showAuthor: true, allowEdit: !!state.me?.isAdmin });
const hasVote = Object.prototype.hasOwnProperty.call(votesMap, s.id); const hasVote = Object.prototype.hasOwnProperty.call(votesMap, s.id);
const current = hasVote ? votesMap[s.id] : 5; // start neutral when no prior vote const current = hasVote ? votesMap[s.id] : 5; // start neutral when no prior vote
const displayScore = hasVote ? current : "—";
const displayEmoji = hasVote ? scoreToEmoji(current) : neutralEmoji();
const footer = document.createElement("div"); const footer = document.createElement("div");
footer.className = "vote-controls"; footer.className = "vote-controls";
footer.innerHTML = ` footer.innerHTML = `
<input class="full-slider" type="range" min="0" max="10" value="${current}" data-id="${s.id}"> <input class="full-slider" type="range" min="0" max="10" value="${current}" data-id="${s.id}">
<span class="score" id="score-${s.id}">${current}</span> <span class="score" id="score-${s.id}">${displayScore}</span>
<span class="score-emoji" id="emoji-${s.id}">${scoreToEmoji(current)}</span>`; <span class="score-emoji" id="emoji-${s.id}">${displayEmoji}</span>`;
li.querySelector(".card-body").appendChild(footer); li.querySelector(".card-body").appendChild(footer);
list.appendChild(li); list.appendChild(li);
}); });
@@ -241,6 +251,14 @@ function syncVoteScores() {
if (emoji) emoji.textContent = scoreToEmoji(score); if (emoji) emoji.textContent = scoreToEmoji(score);
} }
}); });
document.querySelectorAll("input[type=range][data-id]").forEach((slider) => {
const id = slider.dataset.id;
if (Object.prototype.hasOwnProperty.call(votesMap, Number(id))) return;
const scoreLabel = $("score-" + id);
const emoji = $("emoji-" + id);
if (scoreLabel) scoreLabel.textContent = "—";
if (emoji) emoji.textContent = neutralEmoji();
});
} }
function renderResults() { function renderResults() {
@@ -482,9 +500,7 @@ async function refreshPhaseData() {
await loadState(); await loadState();
await Promise.all([loadSuggestData(), loadRevealData(), loadResults()]); await Promise.all([loadSuggestData(), loadRevealData(), loadResults()]);
if (state.phase === "Vote") { if (state.phase === "Vote") {
if (!state.votesRendered) { if (!state.votesRendered) await loadVoteData();
await loadVoteData();
}
} else { } else {
state.votesRendered = false; state.votesRendered = false;
await loadVoteData(); await loadVoteData();
@@ -702,6 +718,7 @@ function normalizeSuggestionForm(formData) {
} }
function scoreToEmoji(score) { function scoreToEmoji(score) {
if (score == null || Number.isNaN(score)) return neutralEmoji();
if (score < 1) return "😡"; if (score < 1) return "😡";
if (score <= 3) return "😠"; if (score <= 3) return "😠";
if (score <= 6) return "😐"; if (score <= 6) return "😐";
@@ -709,3 +726,23 @@ function scoreToEmoji(score) {
if (score <= 9) return "😃"; if (score <= 9) return "😃";
return "🤩"; return "🤩";
} }
function neutralEmoji() {
return "😐";
}
function signatureSuggestions(list) {
return JSON.stringify(
list.map((s) => [
s.id,
s.name,
s.genre,
s.description,
s.screenshotUrl,
s.youtubeUrl,
s.gameUrl,
s.minPlayers,
s.maxPlayers
])
);
}

View File

@@ -175,6 +175,7 @@ h3 { margin: 0; font-size: 18px; }
.card-title-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; min-width: 0; } .card-title-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; min-width: 0; }
.card-title { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .card-title { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.title-meta { display: flex; align-items: center; gap: 8px; } .title-meta { display: flex; align-items: center; gap: 8px; }
.title-meta .chip { max-width: 140px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
p { margin: 0; } p { margin: 0; }
.muted { color: #7a6a53; margin: 0; } .muted { color: #7a6a53; margin: 0; }
.link { color: #30afea; text-decoration: none; font-weight: 700; } .link { color: #30afea; text-decoration: none; font-weight: 700; }