Add voter tooltips across results emojis and average
This commit is contained in:
@@ -146,6 +146,10 @@ Die Ergebnisse bleiben verborgen, bis ein Admin sie freigibt. Danach werden alle
|
||||
|
||||
Nein. Vorschläge und Bewertungen sind schreibgeschützt. Wende dich bei Bedarf an einen Admin.
|
||||
|
||||
### Wie sehe ich, wer für ein Spiel abgestimmt hat?
|
||||
|
||||
Fahre mit der Maus über den Durchschnittswert oder ein Bewertungs-Emoji in der Ergebniszeile, um die alphabetisch sortierte Liste der Abstimmenden zu sehen.
|
||||
|
||||
## Admin-Tools (Für Hosts)
|
||||
|
||||
### Was können Admin-Konten tun?
|
||||
|
||||
@@ -150,6 +150,10 @@ If needed, an admin can close the Results: players with at least one own suggest
|
||||
|
||||
No. Suggestions and votes are read-only. Contact an admin for assistance.
|
||||
|
||||
### How can I see who voted for a game?
|
||||
|
||||
Hover the average score or any score emoji in that result row to see the voter list (sorted alphabetically).
|
||||
|
||||
## Admin Tools (For Hosts)
|
||||
|
||||
### What can admin accounts do?
|
||||
|
||||
@@ -91,6 +91,8 @@
|
||||
"results.average": "Ø",
|
||||
"results.votesList": "All votes",
|
||||
"results.myVote": "Your vote",
|
||||
"results.votersTooltip": "Voted by: {users}",
|
||||
"results.votersTooltipEmpty": "No votes yet",
|
||||
"results.links": "Links",
|
||||
"results.link.site": "Site ↗",
|
||||
"results.link.youtube": "YouTube ↗",
|
||||
@@ -264,6 +266,8 @@
|
||||
"results.average": "Ø",
|
||||
"results.votesList": "Alle Stimmen",
|
||||
"results.myVote": "Deine Stimme",
|
||||
"results.votersTooltip": "Abgestimmt von: {users}",
|
||||
"results.votersTooltipEmpty": "Noch keine Stimmen",
|
||||
"results.links": "Links",
|
||||
"results.link.site": "Webseite ↗",
|
||||
"results.link.youtube": "YouTube ↗",
|
||||
|
||||
@@ -63,6 +63,12 @@ export function renderResults() {
|
||||
const safeShot = safeUrl(r.screenshotUrl);
|
||||
const safeGameUrl = safeUrl(r.gameUrl);
|
||||
const safeYoutubeUrl = safeUrl(r.youtubeUrl);
|
||||
const votersTooltip = buildVotersTooltip(r);
|
||||
const safeVotersTooltip = escapeHtml(votersTooltip);
|
||||
const averageScore =
|
||||
r.average?.toFixed && typeof r.average === "number"
|
||||
? r.average.toFixed(1)
|
||||
: r.average;
|
||||
row.innerHTML = `
|
||||
<td class="rank-cell"><span class="medal">${medal}</span></td>
|
||||
<td class="game-cell">
|
||||
@@ -76,9 +82,9 @@ export function renderResults() {
|
||||
</div>
|
||||
</td>
|
||||
<td class="author-cell">${safeAuthor || "—"}</td>
|
||||
<td>${r.average?.toFixed ? r.average.toFixed(1) : r.average}</td>
|
||||
<td>${formatVotes(r.votes)}</td>
|
||||
<td>${formatMyVote(r.myVote)}</td>
|
||||
<td><span title="${safeVotersTooltip}">${averageScore}</span></td>
|
||||
<td>${formatVotes(r.votes, votersTooltip)}</td>
|
||||
<td>${formatMyVote(r.myVote, votersTooltip)}</td>
|
||||
<td>
|
||||
${safeGameUrl ? `<a class="link compact" href="${safeGameUrl}" target="_blank" rel="noopener">${t("results.link.site")}</a><br>` : ""}
|
||||
${safeYoutubeUrl ? `<a class="link compact" href="${safeYoutubeUrl}" target="_blank" rel="noopener">${t("results.link.youtube")}</a>` : ""}
|
||||
@@ -110,13 +116,36 @@ function buildResultMeta(r) {
|
||||
return `<div class="muted small">${bits.join(" • ")}</div>`;
|
||||
}
|
||||
|
||||
function formatVotes(votes) {
|
||||
if (!Array.isArray(votes) || votes.length === 0) return "⚠️";
|
||||
function formatVotes(votes, tooltip) {
|
||||
const safeTooltip = escapeHtml(tooltip);
|
||||
if (!Array.isArray(votes) || votes.length === 0) {
|
||||
return `<span class="score-emoji" title="${safeTooltip}">⚠️</span>`;
|
||||
}
|
||||
const sorted = [...votes].sort((a, b) => a - b);
|
||||
return sorted.map((v) => scoreToEmoji(v)).join("");
|
||||
return sorted
|
||||
.map(
|
||||
(v) =>
|
||||
`<span class="score-emoji" title="${safeTooltip}">${scoreToEmoji(v)}</span>`,
|
||||
)
|
||||
.join("");
|
||||
}
|
||||
|
||||
function formatMyVote(score) {
|
||||
function formatMyVote(score, tooltip) {
|
||||
if (score == null || Number.isNaN(score)) return "—";
|
||||
return `${score} ${scoreToEmoji(score)}`;
|
||||
const safeTooltip = escapeHtml(tooltip);
|
||||
return `${score} <span class="score-emoji" title="${safeTooltip}">${scoreToEmoji(score)}</span>`;
|
||||
}
|
||||
|
||||
function buildVotersTooltip(result) {
|
||||
const voterNames = Array.isArray(result?.voterNames)
|
||||
? result.voterNames
|
||||
.filter(
|
||||
(name) => typeof name === "string" && name.trim().length > 0,
|
||||
)
|
||||
.sort((a, b) =>
|
||||
a.localeCompare(b, undefined, { sensitivity: "base" }),
|
||||
)
|
||||
: [];
|
||||
if (voterNames.length === 0) return t("results.votersTooltipEmpty");
|
||||
return t("results.votersTooltip", { users: voterNames.join(", ") });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user