diff --git a/wwwroot/app.js b/wwwroot/app.js index 6e68bf8..bdb9367 100644 --- a/wwwroot/app.js +++ b/wwwroot/app.js @@ -12,6 +12,7 @@ const state = { counts: null, mySuggestions: [], allSuggestions: [], + allSuggestionsSig: null, myVotes: [], results: [], votesRendered: false @@ -114,9 +115,16 @@ async function loadSuggestData() { async function loadRevealData() { 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(); 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 hasVote = Object.prototype.hasOwnProperty.call(votesMap, s.id); 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"); footer.className = "vote-controls"; footer.innerHTML = ` - ${current} - ${scoreToEmoji(current)}`; + ${displayScore} + ${displayEmoji}`; li.querySelector(".card-body").appendChild(footer); list.appendChild(li); }); @@ -241,6 +251,14 @@ function syncVoteScores() { 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() { @@ -482,9 +500,7 @@ async function refreshPhaseData() { await loadState(); await Promise.all([loadSuggestData(), loadRevealData(), loadResults()]); if (state.phase === "Vote") { - if (!state.votesRendered) { - await loadVoteData(); - } + if (!state.votesRendered) await loadVoteData(); } else { state.votesRendered = false; await loadVoteData(); @@ -702,6 +718,7 @@ function normalizeSuggestionForm(formData) { } function scoreToEmoji(score) { + if (score == null || Number.isNaN(score)) return neutralEmoji(); if (score < 1) return "😡"; if (score <= 3) return "😠"; if (score <= 6) return "😐"; @@ -709,3 +726,23 @@ function scoreToEmoji(score) { if (score <= 9) 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 + ]) + ); +} diff --git a/wwwroot/styles.css b/wwwroot/styles.css index 7d84b55..f8b02db 100644 --- a/wwwroot/styles.css +++ b/wwwroot/styles.css @@ -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 { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .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; } .muted { color: #7a6a53; margin: 0; } .link { color: #30afea; text-decoration: none; font-weight: 700; }