pull/32/head
eric 2025-08-10 20:17:06 +02:00
parent e27b6f2a23
commit a79f44b24e
2 changed files with 295 additions and 202 deletions

View File

@ -1,162 +1,229 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="de"> <html lang="de">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Spotify Roulette Spiel</title> <title>Spotify Roulette Spiel</title>
<!-- Font -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap" rel="stylesheet">
<style> <style>
:root{ :root{
--primary-color: #1DB954; --bg:#121212; --elev:#181818; --border:#282828; --text:#fff; --muted:#b3b3b3;
--bg-light: #121212; /* vorher: #f2f2f2 */ --accent:#1db954; --accent-press:#169e47; --glow:rgba(29,185,84,.25);
--bg-dark: #000000; /* vorher: #121212 */ --radius:16px; --shadow:0 10px 30px rgba(0,0,0,.35);
--text-light: #eaeaea; /* vorher: #ffffff */
--text-dark: #eaeaea; /* vorher: #333333 */
--accent: #191414;
--option-circle-size: 80%;
--option-max-size: 400px;
--option-radius: 120px;
} }
* { box-sizing: border-box; margin: 0; padding: 0; } *{box-sizing:border-box}
html,body{height:100%}
body{ body{
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; margin:0; font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial;
background: var(--bg-light); color:var(--text);
color: var(--text-dark); background:
display: grid; radial-gradient(1200px 600px at 20% -10%, rgba(29,185,84,.15), transparent 60%),
grid-template-areas: radial-gradient(1000px 500px at 100% -20%, rgba(29,185,84,.12), transparent 70%),
"header header" var(--bg);
"main aside"
"footer footer";
grid-template-columns: 3fr 1fr;
grid-template-rows: auto 1fr auto;
min-height: 100vh;
} }
.container{max-width:1100px; margin:0 auto; padding:24px}
/* Header */
header{ header{
grid-area: header; display:flex; align-items:center; justify-content:space-between; gap:16px;
background: var(--primary-color); padding-bottom:12px; border-bottom:1px solid var(--border);
color: var(--text-light);
padding: 1rem 2rem;
display: flex;
align-items: center;
justify-content: space-between;
} }
main { grid-area: main; padding: 2rem; } .brand{display:flex; align-items:center; gap:12px}
aside { .logo{width:36px; height:36px; border-radius:50%; background:var(--accent); color:#121212;
grid-area: aside; display:grid; place-content:center; box-shadow:0 0 0 6px var(--glow)}
background: var(--accent); h1{margin:0; font-size:20px; letter-spacing:.2px}
color: var(--text-light); .pill{display:inline-flex; align-items:center; gap:8px; padding:6px 10px; border-radius:999px;
padding: 1.5rem; border:1px solid var(--border); background:#101010; color:var(--muted); font-weight:600}
overflow-y: auto; .pill b{color:#fff; letter-spacing:.08em}
}
footer {
grid-area: footer;
background: var(--accent);
color: var(--text-light);
text-align: center;
padding: 0.5rem;
font-size: 0.9rem;
}
.section-title {
margin-bottom: 1rem;
font-size: 1.2rem;
border-bottom: 2px solid var(--primary-color);
padding-bottom: 0.3rem;
}
ul { list-style: none; }
#controls, .round-controls { margin: 1rem 0; text-align: center; }
button {
background: var(--primary-color);
border: none;
color: var(--text-light);
padding: 0.6rem 1.2rem;
margin: 0.3rem;
font-size: 1rem;
border-radius: 20px;
cursor: pointer;
transition: background 0.2s;
}
button:hover:not(:disabled) { background: #0c903a; }
button:disabled { opacity: 0.6; cursor: not-allowed; }
/* Rundenbereich */ .row{display:flex; gap:10px; align-items:center; flex-wrap:wrap}
#roundArea { .btn{border:0; padding:10px 14px; border-radius:999px; font-weight:700; cursor:pointer;
margin-top: 1rem; transition:transform .06s, filter .15s, background .2s}
background: var(--bg-dark); .btn:active{transform:translateY(1px)}
color: var(--text-light); .btn-primary{background:var(--accent); color:#0a0a0a}
padding: 0.7rem 1rem; .btn-primary:hover{filter:brightness(1.05)}
border-radius: 6px; .btn-primary:active{background:var(--accent-press)}
box-shadow: 0 2px 8px rgba(0,0,0,0.1); .btn-ghost{background:transparent; color:var(--text); border:1px solid var(--border)}
text-align: center; .btn-ghost:hover{border-color:var(--muted)}
} .muted{color:var(--muted)}
#songEmbed iframe { border-radius: 6px; }
:root { /* Grid */
/* 80% der Haupt-Container-Breite, max. 400px */ .grid{display:grid; grid-template-columns:1.1fr .9fr; gap:24px; margin-top:20px}
--option-circle-size: 80%; @media (max-width:980px){.grid{grid-template-columns:1fr}}
--option-max-size: 400px;
}
/* Cards */
.card{background:var(--elev); border:1px solid var(--border); border-radius:var(--radius); box-shadow:var(--shadow)}
.card-hd{padding:18px 18px 8px; display:flex; align-items:center; justify-content:space-between}
.card-bd{padding:16px 18px 20px}
/* Now Playing */
.now{display:grid; grid-template-columns:96px 1fr; gap:16px; align-items:center}
.cover{width:96px; height:96px; border-radius:10px; background:#0d0d0d; border:1px solid var(--border);
overflow:hidden; display:grid; place-content:center}
.cover img{width:100%; height:100%; object-fit:cover}
.track-title{font-size:18px; font-weight:700; margin:0 0 2px}
.artists{color:var(--muted); margin:0 0 6px}
#songEmbed iframe{border-radius:10px; width:100%; height:80px; border:0}
/* Round area (Kreis) */
#roundArea[hidden]{display:none}
#options{ #options{
position: relative; position:relative; width:100%; max-width:520px; aspect-ratio:1; margin:14px 0 6px;
width: var(--option-circle-size); border-radius:50%; border:1px dashed var(--border);
max-width: var(--option-max-size); background: radial-gradient(240px 240px at 50% 50%, rgba(29,185,84,.08), transparent 60%);
aspect-ratio: 1; /* Höhe = Breite */
margin: 2rem auto;
} }
.player-option{
#options .player-option {
position:absolute; position:absolute;
width: 100px; left:50%; /* neu */
height: 40px; top:50%; /* neu */
left: 50%; transform-origin:center;
top: 50%; background:#0f0f0f; color:var(--text);
transform-origin: center calc(-50% - 10px); border:1px solid var(--border);
text-align: center; border-radius:999px; padding:8px 12px;
padding: 0.5rem; font-weight:700;
border-radius: 4px; white-space:nowrap;
transition:filter .15s, transform .06s, background .2s, box-shadow .2s;
box-shadow:0 0 0 0 var(--glow);
} }
#options .player-option:nth-child(1) { transform: rotate(0deg) translate(0, var(--option-radius)); } .player-option:hover{filter:brightness(1.1)}
#options .player-option:nth-child(2) { transform: rotate(90deg) translate(0, var(--option-radius)); } .player-option:active{transform:scale(.98)}
#options .player-option:nth-child(3) { transform: rotate(180deg) translate(0, var(--option-radius)); } .player-option:disabled{opacity:.7}
#options .player-option:nth-child(4) { transform: rotate(270deg) translate(0, var(--option-radius)); } .player-option.correct{background:var(--accent); color:#0a0a0a; box-shadow:0 0 0 6px var(--glow)}
/* Players + Scoreboard */
#playersList{list-style:none; padding:0; margin:0; display:grid;
grid-template-columns:repeat(auto-fill, minmax(200px,1fr)); gap:12px}
#playersList > *{display:flex; align-items:center; gap:10px; padding:10px;
border:1px solid var(--border); border-radius:12px; background:#101010}
#scoreboard{list-style:none; padding:0; margin:0}
#scoreboard li{display:flex; justify-content:space-between; padding:10px 4px; border-bottom:1px solid var(--border)}
/* Aside / Device select */
aside{background:transparent}
.section-title{font-weight:700; margin:0 0 10px}
#songList{list-style:none; padding:0; margin:0 0 14px; display:flex; flex-direction:column; gap:8px}
#songList li{padding:10px 12px; border:1px solid var(--border); border-radius:10px; background:#101010}
#deviceSelectArea label{color:var(--muted); font-size:13px}
#deviceSelect{
width:100%; margin-top:8px;
background:#0f0f0f; color:#fff; border:1px solid var(--border); border-radius:12px; padding:10px 12px; outline:none;
}
#deviceSelect:focus{border-color:var(--accent); box-shadow:0 0 0 3px var(--glow)}
/* Toast (Clipboard-Notice) */
.toast{position:fixed; left:50%; bottom:22px; transform:translateX(-50%);
background:#101010; color:#fff; border:1px solid var(--border);
padding:10px 14px; border-radius:999px; box-shadow:var(--shadow); z-index:50; display:none}
.toast.show{display:block}
/* Slide-out Drawer für Songliste (wie im Beispiel) */
.drawer{position:fixed; top:0; right:0; height:100%; width:340px; background:#0f0f0f;
border-left:1px solid var(--border); transform:translateX(100%); transition:transform .25s ease;
z-index:60; display:flex; flex-direction:column}
.drawer.open{transform:translateX(0)}
.drawer header{padding:16px; border-bottom:1px solid var(--border); display:flex; justify-content:space-between; align-items:center}
.drawer .list{padding:12px 16px; overflow:auto}
.drawer .track{display:flex; align-items:center; gap:10px; padding:8px 6px; border-radius:10px}
.drawer .track:hover{background:#141414}
.drawer .tcover{width:40px; height:40px; border-radius:6px; background:#1a1a1a; border:1px solid var(--border)}
.drawer .meta{display:flex; flex-direction:column}
.drawer .meta b{font-size:13px}
.drawer .meta span{font-size:12px; color:var(--muted)}
</style> </style>
</head> </head>
<body> <body>
<div class="container">
<header> <header>
<div class="brand">
<div class="logo" aria-hidden="true">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
<path d="M12 2 2 7l10 5 10-5-10-5Zm10 7-10 5v9l10-5V9ZM2 9v9l10 5v-9L2 9Z"/>
</svg>
</div>
<h1>Spotify Roulette</h1> <h1>Spotify Roulette</h1>
<div>Spiel-Code: <span id="gameId" class="game-code"></span></div> </div>
<div class="row">
<span class="pill">Spiel-Code: <b id="gameId"></b></span>
<button id="toggleSongList" class="btn btn-ghost" title="Zuletzt gehörte Songs">Songliste</button>
</div>
</header> </header>
<main>
<section> <div class="grid">
<div class="section-title">Teilnehmer</div> <!-- LEFT: Now Playing + Runde -->
<ul id="playersList"></ul> <section class="card">
</section> <div class="card-hd">
<div id="controls"> <h2 style="margin:0">Now Playing</h2>
<button id="startRound">Runde starten</button> <div class="row">
<button id="startRound" class="btn btn-ghost">Runde starten</button>
</div> </div>
</div>
<div class="card-bd">
<!-- <div class="now" style="margin-bottom:10px">-->
<!-- <div class="cover" aria-hidden="true">-->
<!-- <svg width="48" height="48" viewBox="0 0 24 24" fill="#333"><path d="M12 3a9 9 0 1 0 .001 18.001A9 9 0 0 0 12 3Zm0 2a7 7 0 1 1-.001 14.001A7 7 0 0 1 12 5Zm0 3a4 4 0 1 0 .001 8.001A4 4 0 0 0 12 8Z"/></svg>-->
<!-- </div>-->
<!-- <div>-->
<!-- <p id="trackTitle" class="track-title">—</p>-->
<!-- <p id="trackArtists" class="artists">—</p>-->
<!-- </div>-->
<!-- </div>-->
<!-- IDs exakt wie dein game.js -->
<section id="roundArea" hidden> <section id="roundArea" hidden>
<div class="section-title">Wer hats gehört?</div> <div id="songEmbed" style="margin-bottom:12px"></div>
<div id="songEmbed"></div> <div id="options"></div>
<div class="round-controls" id="options"> <div id="result" class="muted" style="margin-top:10px"></div>
<!-- Buttons zu Auswahl verteilen: JS erzeugt <button class="player-option">Name</button> --> <button id="nextRound" class="btn btn-primary" hidden>Nächster Song</button>
</section>
</div> </div>
<div id="result"></div>
<button id="nextRound" hidden>Weiter</button>
</section> </section>
<section>
<div class="section-title">Scoreboard</div> <!-- RIGHT: Teilnehmer + Scoreboard -->
<section class="card">
<div class="card-hd">
<h2 style="margin:0">Teilnehmer</h2>
<button id="refreshPlayers" class="btn btn-ghost">Aktualisieren</button>
</div>
<div class="card-bd">
<ul id="playersList"></ul>
</div>
<div class="card-hd"><h2 style="margin:0">Scoreboard</h2></div>
<div class="card-bd">
<ul id="scoreboard"></ul> <ul id="scoreboard"></ul>
</div>
</section> </section>
</main> </div>
<aside>
<div class="section-title">Geladene Songs</div> <!-- ASIDE: Geladene Songs + Wiedergabegerät -->
<ul id="songList"></ul>
<div id="deviceSelectArea"></div> <div id="deviceSelectArea"></div>
</aside> </aside>
<footer> Spotify Roulette alles ausser arbeiten </footer> </div>
<!-- Slide-out Drawer für Songliste -->
<aside class="drawer" id="songDrawer" aria-label="Zuletzt gehörte Songs">
<header>
<h3 style="margin:0">Zuletzt gehört</h3>
<button class="btn btn-ghost" id="closeDrawer">Schließen</button>
</header>
<div class="list" id="songListArea"></div>
</aside>
<!-- Toast für Clipboard-Notice -->
<div id="toast" class="toast" role="status" aria-live="polite"></div>
<!-- Deine existierenden Module -->
<script type="module" src="/js/utils.js"></script> <script type="module" src="/js/utils.js"></script>
<script type="module" src="/js/start-round.js"></script> <script type="module" src="/js/start-round.js"></script>
<script type="module" src="/js/device-select.js"></script> <!-- füllt #deviceSelectArea -->
<script type="module" src="/js/game.js"></script> <script type="module" src="/js/game.js"></script>
<script type="module" src="/js/device-select.js"></script>
</body> </body>
</html> </html>

View File

@ -6,17 +6,31 @@ import { setupStartRound } from "./start-round.js";
const gameId = getParam("gameId"); const gameId = getParam("gameId");
const username = getParam("username"); const username = getParam("username");
// --- kleine Helper ---
function showToast(msg, ms = 2200) {
const t = document.getElementById("toast");
if (!t) return;
t.textContent = msg;
t.classList.add("show");
setTimeout(() => t.classList.remove("show"), ms);
}
function esc(s) {
const d = document.createElement("div");
d.textContent = String(s ?? "");
return d.innerHTML;
}
// 1) Parameter prüfen // 1) Parameter prüfen
if (!gameId || !username) { if (!gameId || !username) {
alert("Ungültige oder fehlende URL-Parameter!"); alert("Ungültige oder fehlende URL-Parameter!");
throw new Error("Missing gameId or username"); throw new Error("Missing gameId or username");
} }
// 2) Copy to clipboard (unverändert) // 2) Copy to clipboard (unverändert, aber mit Toast)
(function copyCodeToClipboard(code) { (function copyCodeToClipboard(code) {
if (navigator.clipboard && window.isSecureContext) { if (navigator.clipboard && window.isSecureContext) {
navigator.clipboard.writeText(code) navigator.clipboard.writeText(code)
.then(() => console.log(`GameCode ${code} copied to clipboard`)) .then(() => showToast(`Spiel-Code ${code} kopiert`))
.catch(err => console.error("Clipboard write failed:", err)); .catch(err => console.error("Clipboard write failed:", err));
} else { } else {
const ta = document.createElement("textarea"); const ta = document.createElement("textarea");
@ -28,7 +42,7 @@ if (!gameId || !username) {
ta.select(); ta.select();
try { try {
document.execCommand("copy"); document.execCommand("copy");
console.log(`GameCode ${code} copied via execCommand`); showToast(`Spiel-Code ${code} kopiert`);
} catch (err) { } catch (err) {
console.error("Fallback copy failed:", err); console.error("Fallback copy failed:", err);
} }
@ -36,16 +50,14 @@ if (!gameId || !username) {
} }
})(gameId); })(gameId);
// 3) Visuelles Feedback (unverändert) // 3) Spiel-Code ins DOM schreiben
const notice = document.createElement("div");
notice.textContent = `Spiel-Code ${gameId} in die Zwischenablage kopiert!`;
Object.assign(notice.style, { /* … Styles … */ });
document.body.append(notice);
setTimeout(() => notice.remove(), 3000);
// 4) Spiel-Code ins DOM schreiben
document.getElementById("gameId").textContent = gameId; document.getElementById("gameId").textContent = gameId;
// 4) Drawer toggles für Songliste
const drawer = document.getElementById("songDrawer");
document.getElementById("toggleSongList")?.addEventListener("click", () => drawer?.classList.add("open"));
document.getElementById("closeDrawer")?.addEventListener("click", () => drawer?.classList.remove("open"));
// 5) WebSocket mit Reconnect-Logik // 5) WebSocket mit Reconnect-Logik
let socket; let socket;
function connectWebSocket() { function connectWebSocket() {
@ -57,7 +69,7 @@ function connectWebSocket() {
socket.addEventListener("open", () => { socket.addEventListener("open", () => {
console.log("WebSocket connected. Requesting player list..."); console.log("WebSocket connected. Requesting player list...");
socket.send(JSON.stringify({ type: "requestPlayers" })); socket.send(JSON.stringify({ type: "requestPlayers" }));
setupStartRound(socket); setupStartRound(socket); // deaktiviert den Start-Button beim Klicken
}); });
socket.addEventListener("message", async ({ data }) => { socket.addEventListener("message", async ({ data }) => {
@ -66,7 +78,6 @@ function connectWebSocket() {
switch (msg.type) { switch (msg.type) {
case "players": case "players":
console.log("Empfangene Spieler:", msg.players);
renderList("#playersList", msg.players, username); renderList("#playersList", msg.players, username);
break; break;
case "reload": case "reload":
@ -106,13 +117,9 @@ const optionsDiv = document.getElementById("options");
const resultP = document.getElementById("result"); const resultP = document.getElementById("result");
const scoreboard = document.getElementById("scoreboard"); const scoreboard = document.getElementById("scoreboard");
// 8) Funktion zum Anzeigen einer neuen Runde // 6) Neue Runde anzeigen
//let playLock = false; async function handleRoundStart({ ownerOptions, songUri, trackInfos }) {
async function handleRoundStart({ownerOptions, songUri, allTracks, trackInfos}) {
// UI zurücksetzen // UI zurücksetzen
//if (playLock) return; // Verhindert parallele Ausführung
//playLock = true;
resultP.textContent = ""; resultP.textContent = "";
optionsDiv.innerHTML = ""; optionsDiv.innerHTML = "";
songEmbed.innerHTML = ""; songEmbed.innerHTML = "";
@ -126,26 +133,26 @@ async function handleRoundStart({ownerOptions, songUri, allTracks, trackInfos})
allow="encrypted-media"> allow="encrypted-media">
</iframe>`; </iframe>`;
// Spotify-Playback (optional, wenn vorhanden)
if (window.playOnSpotify && typeof window.playOnSpotify === "function") { if (window.playOnSpotify && typeof window.playOnSpotify === "function") {
await window.playOnSpotify(songUri, username); // Warten bis fertig await window.playOnSpotify(songUri, username);
} }
//if (window.playOnSpotify && typeof window.playOnSpotify === "function") { // Buttons kreisförmig verteilen
// window.playOnSpotify(songUri, username); requestAnimationFrame(() => {
//}
// Dynamische Kreisverteilung der Buttons
// Warten, bis #options gerendert ist
setTimeout(() => {
const optsRect = optionsDiv.getBoundingClientRect(); const optsRect = optionsDiv.getBoundingClientRect();
const radius = Math.min(optsRect.width, optsRect.height) / 2 - 50; // 50px Abstand zum Rand const radius = Math.min(optsRect.width, optsRect.height) / 2 - 40; // Abstand zum Rand
ownerOptions.forEach((user, i) => { ownerOptions.forEach((user, i) => {
const btn = document.createElement("button"); const btn = document.createElement("button");
btn.textContent = user; btn.textContent = user;
btn.classList.add("player-option"); btn.classList.add("player-option");
const angle = 360 * i / ownerOptions.length; //const angle = 360 * i / ownerOptions.length;
btn.style.transform = `rotate(${angle}deg) translateY(-${radius}px) rotate(${-angle}deg)`; //btn.style.transform = `rotate(${angle}deg) translateY(-${radius}px) rotate(${-angle}deg)`;
const angle = (360 / ownerOptions.length) * i;
// zentrieren -> dann drehen -> nach außen schieben -> Text aufrecht drehen
btn.style.transform = `translate(-50%, -50%) rotate(${angle}deg) translate(0, -${radius}px) rotate(${-angle}deg)`;
btn.addEventListener("click", () => { btn.addEventListener("click", () => {
socket.send(JSON.stringify({ socket.send(JSON.stringify({
type: "guess", type: "guess",
@ -156,24 +163,42 @@ async function handleRoundStart({ownerOptions, songUri, allTracks, trackInfos})
}); });
optionsDiv.appendChild(btn); optionsDiv.appendChild(btn);
}); });
}, 0); });
// Start-Button ausblenden + Rundensektion einblenden
startBtn.hidden = true; startBtn.hidden = true;
startBtn.disabled = true;
roundArea.hidden = false; roundArea.hidden = false;
// Geladene Songs (beide Orte befüllen: Aside + Drawer)
const songList = document.getElementById("songList"); const songList = document.getElementById("songList");
songList.innerHTML = ""; const songListArea = document.getElementById("songListArea");
//trackinfos ist eine map bestehend aus aus Spielername und Liste von Track-Infos
const userTracks = trackInfos?.[username] ?? []; const userTracks = trackInfos?.[username] ?? [];
userTracks.forEach(trackInfo => {
if (songList) {
songList.innerHTML = "";
userTracks.forEach(t => {
const li = document.createElement("li"); const li = document.createElement("li");
li.textContent = trackInfo; li.textContent = t;
songList.appendChild(li); songList.appendChild(li);
}); });
//playLock = false; }
if (songListArea) {
songListArea.innerHTML = "";
userTracks.forEach(t => {
// Erwartetes Format "Titel - Künstler"
const [title, artists] = String(t).split(" - ");
const row = document.createElement("div");
row.className = "track";
row.innerHTML = `
<div class="tcover"></div>
<div class="meta"><b>${esc(title ?? t)}</b><span>${esc(artists ?? "")}</span></div>`;
songListArea.appendChild(row);
});
}
} }
// 9) Funktion zum Anzeigen des Ergebnisses // 7) Ergebnis + Weiter-Button
function renderScoreboard(scores) { function renderScoreboard(scores) {
scoreboard.innerHTML = ""; scoreboard.innerHTML = "";
Object.entries(scores).forEach(([user, pts]) => { Object.entries(scores).forEach(([user, pts]) => {
@ -191,9 +216,8 @@ function handleRoundResult({ scores, guesses, owner }) {
const correct = guess === owner; const correct = guess === owner;
const icon = correct ? "✅" : "❌"; const icon = correct ? "✅" : "❌";
const delta = correct ? "+3" : "-1"; const delta = correct ? "+3" : "-1";
const msg = `${icon} ${user} hat auf ${guess} getippt${correct ? " (richtig!)" : " (falsch)"} [${delta}]`;
const p = document.createElement("p"); const p = document.createElement("p");
p.textContent = msg; p.textContent = `${icon} ${user} hat auf ${guess} getippt${correct ? " (richtig!)" : " (falsch)"} [${delta}]`;
resultP.appendChild(p); resultP.appendChild(p);
}); });
@ -211,18 +235,18 @@ function handleRoundResult({ scores, guesses, owner }) {
}; };
} }
// 8) Spielende -> Start-Button zurück
function handleGameEnd({ winner }) { function handleGameEnd({ winner }) {
resultP.textContent = `🎉 ${winner} hat gewonnen!`; resultP.textContent = `🎉 ${winner} hat gewonnen!`;
setTimeout(() => { setTimeout(() => {
startBtn.hidden = false; startBtn.hidden = false;
roundArea.hidden = true;
startBtn.disabled = false; startBtn.disabled = false;
roundArea.hidden = true;
scoreboard.innerHTML = ""; scoreboard.innerHTML = "";
}, 6000); }, 6000);
} }
// Spotify-Playback Funktion // Spotify-Playback Funktion (unverändert)
// game.js
async function playOnSpotify(trackUri, username) { async function playOnSpotify(trackUri, username) {
const deviceId = document.getElementById("deviceSelect")?.value; const deviceId = document.getElementById("deviceSelect")?.value;
if (!deviceId) { if (!deviceId) {
@ -244,3 +268,5 @@ async function playOnSpotify(trackUri, username) {
} }
} }
window.playOnSpotify = playOnSpotify; window.playOnSpotify = playOnSpotify;