diff --git a/client/src/common/styles/components/lobby-screen.sass b/client/src/common/styles/components/lobby-screen.sass
index 8b23d75..40803a6 100644
--- a/client/src/common/styles/components/lobby-screen.sass
+++ b/client/src/common/styles/components/lobby-screen.sass
@@ -179,13 +179,27 @@
padding: 2rem
width: 90%
max-width: 500px
+ border: 6px solid #000
+ box-shadow: 10px 10px 0 rgba(0, 0, 0, 0.5)
+ animation: modal-appear 0.3s ease-out
h2
margin-top: 0
color: $primary
+ font-family: 'Press Start 2P', monospace
+ font-size: 1.4rem
+ margin-bottom: 1.5rem
.modal-actions
display: flex
justify-content: flex-end
gap: 1rem
margin-top: 2rem
+
+ @keyframes modal-appear
+ 0%
+ transform: scale(0.8)
+ opacity: 0
+ 100%
+ transform: scale(1)
+ opacity: 1
diff --git a/client/src/common/styles/components/results-screen.sass b/client/src/common/styles/components/results-screen.sass
index a334a0b..bc9c844 100644
--- a/client/src/common/styles/components/results-screen.sass
+++ b/client/src/common/styles/components/results-screen.sass
@@ -243,6 +243,14 @@
padding: 0.25rem 0.5rem
border-radius: 1rem
background-color: rgba(255, 255, 255, 0.1)
+ margin-right: 0.5rem
+
+ .submitter
+ display: block
+ font-size: 0.8rem
+ color: $text-muted
+ margin-top: 0.5rem
+ font-style: italic
.versus
font-family: 'Bangers', cursive
diff --git a/client/src/common/styles/forms.sass b/client/src/common/styles/forms.sass
index 92adc47..bc89437 100644
--- a/client/src/common/styles/forms.sass
+++ b/client/src/common/styles/forms.sass
@@ -53,17 +53,26 @@
border: 3px solid #000
position: relative
cursor: pointer
+ box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.2)
+ transition: all 0.2s ease
- &:checked:after
- content: ''
- position: absolute
- left: 6px
- top: 2px
- width: 6px
- height: 12px
- border: solid $primary
- border-width: 0 3px 3px 0
- transform: rotate(45deg)
+ &:hover
+ border-color: $secondary
+
+ &:checked
+ background-color: rgba($primary, 0.2)
+ border-color: $primary
+
+ &:after
+ content: ''
+ position: absolute
+ left: 6px
+ top: 2px
+ width: 6px
+ height: 12px
+ border: solid $primary
+ border-width: 0 3px 3px 0
+ transform: rotate(45deg)
&.checkbox
display: flex
@@ -86,6 +95,31 @@
gap: 1rem
margin-top: 1.5rem
+// Checkbox group with helper text
+.checkbox-group
+ display: flex
+ flex-direction: column
+ margin-bottom: 1.5rem
+
+ .checkbox-container
+ display: flex
+ align-items: center
+ margin-bottom: 0.5rem
+
+ input[type="checkbox"]
+ margin-right: 0.75rem
+ min-width: 24px
+ height: 24px
+
+ label
+ margin-bottom: 0
+ margin-top: 0
+ cursor: pointer
+ font-family: 'Press Start 2P', monospace
+ font-size: 0.8rem
+ text-transform: uppercase
+ color: $secondary
+
// Buttons
.btn
display: inline-flex
diff --git a/client/src/components/HomeScreen.jsx b/client/src/components/HomeScreen.jsx
index 42b2af3..4b4b7d7 100644
--- a/client/src/components/HomeScreen.jsx
+++ b/client/src/components/HomeScreen.jsx
@@ -89,7 +89,7 @@ const HomeScreen = () => {
);
diff --git a/client/src/components/LobbyScreen.jsx b/client/src/components/LobbyScreen.jsx
index c1f3a78..181dc73 100644
--- a/client/src/components/LobbyScreen.jsx
+++ b/client/src/components/LobbyScreen.jsx
@@ -2,13 +2,15 @@ import { useState, useEffect } from 'react';
import { useGame } from '../context/GameContext';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faUsers, faGear, faPlay, faCopy, faSignOutAlt } from '@fortawesome/free-solid-svg-icons';
+import { getDisplayName } from '../utils/playerUtils';
const LobbyScreen = () => {
const { lobby, currentPlayer, isHost, updateSettings, startGame, leaveLobby } = useGame();
const [showSettings, setShowSettings] = useState(false);
const [settings, setSettings] = useState({
songsPerPlayer: 4,
- maxPlayers: 10
+ maxPlayers: 10,
+ hidePlayerNames: false
});
const [copied, setCopied] = useState(false);
@@ -64,9 +66,11 @@ const LobbyScreen = () => {
Spieler ({lobby.players.length})
- {lobby.players.map(player => (
+ {lobby.players.map((player, index) => (
-
- {player.name} {player.id === lobby.hostId && '(Gastgeber)'} {player.id === currentPlayer.id && '(Du)'}
+ {getDisplayName(player, lobby, currentPlayer, index)}
+ {player.id === lobby.hostId && ' (Gastgeber)'}
+ {player.id === currentPlayer.id && !lobby.settings.hidePlayerNames && ' (Du)'}
{!player.isConnected && (Getrennt)}
))}
@@ -78,6 +82,7 @@ const LobbyScreen = () => {
Spieleinstellungen
Lieder pro Spieler: {settings.songsPerPlayer}
Maximale Spieler: {settings.maxPlayers}
+ Spielernamen verbergen: {settings.hidePlayerNames ? 'Ja' : 'Nein'}
{isHost && (
+
+
+
+
+
+
+
) : (
diff --git a/client/src/components/SongSubmissionScreen.jsx b/client/src/components/SongSubmissionScreen.jsx
index 052618d..5570356 100644
--- a/client/src/components/SongSubmissionScreen.jsx
+++ b/client/src/components/SongSubmissionScreen.jsx
@@ -2,6 +2,7 @@ import { useState, useEffect, useRef } from 'react';
import { useGame } from '../context/GameContext';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faPlus, faTrash, faCheck, faMusic, faVideoCamera, faSearch, faSpinner, faExternalLinkAlt, faTimes } from '@fortawesome/free-solid-svg-icons';
+import { getDisplayName } from '../utils/playerUtils';
const SongSubmissionScreen = () => {
const { lobby, currentPlayer, addSong, removeSong, setPlayerReady, searchYouTube, getYouTubeMetadata } = useGame();
@@ -502,9 +503,9 @@ const SongSubmissionScreen = () => {
Spieler bereit
- {lobby && lobby.players.map(player => (
+ {lobby && lobby.players.map((player, index) => (
-
- {player.name} {player.id === currentPlayer.id && '(Du)'}
+ {getDisplayName(player, lobby, currentPlayer, index)} {player.id === currentPlayer.id && lobby.settings.hidePlayerNames && '(Du)'}
{player.isReady ? (
) : (
diff --git a/client/src/components/VotingScreen.jsx b/client/src/components/VotingScreen.jsx
index a38475c..7995391 100644
--- a/client/src/components/VotingScreen.jsx
+++ b/client/src/components/VotingScreen.jsx
@@ -4,6 +4,7 @@ import { useGame } from '../context/GameContext';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faVoteYea, faTrophy, faMusic, faCheck, faMedal, faCrown, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
import YouTubeEmbed from './YouTubeEmbed';
+import { getDisplayName } from '../utils/playerUtils';
function VotingScreen() {
const { lobby, currentPlayer, submitVote, isHost, isOffline, isReconnecting } = useGame();
@@ -356,7 +357,7 @@ function VotingScreen() {
return (
-
- {player.name} {player.id === currentPlayer.id && '(Du)'}
+ {getDisplayName(player, lobby, currentPlayer)} {player.id === currentPlayer.id && lobby.settings.hidePlayerNames && '(Du)'}
{hasPlayerVoted &&
}
diff --git a/client/src/utils/playerUtils.js b/client/src/utils/playerUtils.js
new file mode 100644
index 0000000..e797d28
--- /dev/null
+++ b/client/src/utils/playerUtils.js
@@ -0,0 +1,45 @@
+/**
+ * Utility functions for player name handling
+ */
+
+/**
+ * Returns an anonymous name for a player when hidePlayerNames is enabled
+ * @param {string} playerId - The player's ID
+ * @param {number} index - The player's index in a list (optional)
+ * @param {boolean} isCurrentPlayer - Whether this is the current player
+ * @returns {string} - Anonymous name or indicator
+ */
+export const getAnonymousName = (playerId, index = null, isCurrentPlayer = false) => {
+ if (isCurrentPlayer) {
+ return 'Du';
+ }
+
+ // Use index-based naming if provided
+ if (index !== null) {
+ return `Spieler ${index + 1}`;
+ }
+
+ // Use last 4 characters of ID as fallback
+ const shortId = playerId.slice(-4);
+ return `Spieler ${shortId}`;
+};
+
+/**
+ * Returns the appropriate player name based on game settings
+ * @param {Object} player - The player object
+ * @param {Object} lobby - The lobby object
+ * @param {Object} currentPlayer - The current player
+ * @param {number} index - The player's index in a list (optional)
+ * @returns {string} - The player name or anonymous identifier
+ */
+export const getDisplayName = (player, lobby, currentPlayer, index = null) => {
+ const isCurrentPlayer = player.id === currentPlayer?.id;
+
+ // If hidePlayerNames is enabled
+ if (lobby?.settings?.hidePlayerNames) {
+ return getAnonymousName(player.id, index, isCurrentPlayer);
+ }
+
+ // Otherwise return the actual name
+ return player.name;
+};
diff --git a/server/game.js b/server/game.js
index 9d21f9b..e945020 100644
--- a/server/game.js
+++ b/server/game.js
@@ -28,7 +28,8 @@ class GameManager {
settings: {
songsPerPlayer: 3,
maxPlayers: 10,
- minPlayers: 3
+ minPlayers: 3,
+ hidePlayerNames: false
},
players: [{
id: hostId,
@@ -225,6 +226,11 @@ class GameManager {
return { error: 'Max players must be between 3 and 20' };
}
+ // Validate boolean settings
+ if (settings.hidePlayerNames !== undefined && typeof settings.hidePlayerNames !== 'boolean') {
+ settings.hidePlayerNames = Boolean(settings.hidePlayerNames);
+ }
+
// Update settings
lobby.settings = {
...lobby.settings,