Translate UI text to German for Lobby, Results, and Voting screens to enhance localization
All checks were successful
Publish Docker image / Push Docker image to Docker Hub (push) Successful in 1m44s
All checks were successful
Publish Docker image / Push Docker image to Docker Hub (push) Successful in 1m44s
This commit is contained in:
parent
e24ecb418c
commit
4f4626260f
@ -143,7 +143,7 @@ const LobbyScreen = () => {
|
|||||||
Abbrechen
|
Abbrechen
|
||||||
</button>
|
</button>
|
||||||
<button className="btn primary" onClick={handleSaveSettings}>
|
<button className="btn primary" onClick={handleSaveSettings}>
|
||||||
Einstellungen speichern
|
Mach rin
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -9,42 +9,49 @@ const ResultsScreen = () => {
|
|||||||
const [showBattleHistory, setShowBattleHistory] = useState(false);
|
const [showBattleHistory, setShowBattleHistory] = useState(false);
|
||||||
const [confetti, setConfetti] = useState(true);
|
const [confetti, setConfetti] = useState(true);
|
||||||
|
|
||||||
// Winner information
|
// Gewinner-Informationen
|
||||||
const winner = lobby?.finalWinner;
|
const winner = lobby?.finalWinner;
|
||||||
const winnerVideoId = getYouTubeId(winner?.youtubeLink);
|
const winnerVideoId = getYouTubeId(winner?.youtubeLink);
|
||||||
|
|
||||||
// Confetti effect for winner celebration
|
// Konfetti-Effekt für die Siegerfeier
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (confetti) {
|
if (confetti) {
|
||||||
// Create confetti animation
|
// Konfetti-Animation erstellen
|
||||||
const createConfetti = () => {
|
const createConfetti = () => {
|
||||||
const confettiContainer = document.createElement('div');
|
const confettiContainer = document.createElement('div');
|
||||||
confettiContainer.className = 'confetti';
|
confettiContainer.className = 'confetti';
|
||||||
|
|
||||||
// Random position and color
|
// Zufällige Position, Größe und Farbe
|
||||||
const left = Math.random() * 100;
|
const left = Math.random() * 100;
|
||||||
const colors = ['#f94144', '#f3722c', '#f8961e', '#f9c74f', '#90be6d', '#43aa8b', '#577590'];
|
const colors = ['#f94144', '#f3722c', '#f8961e', '#f9c74f', '#90be6d', '#43aa8b', '#577590'];
|
||||||
const color = colors[Math.floor(Math.random() * colors.length)];
|
const color = colors[Math.floor(Math.random() * colors.length)];
|
||||||
|
const size = Math.random() * 0.7 + 0.3; // Zwischen 0.3 und 1rem
|
||||||
|
const rotation = Math.random() * 360; // Zufällige Rotation
|
||||||
|
const duration = Math.random() * 3 + 2; // Zwischen 2 und 5 Sekunden
|
||||||
|
|
||||||
confettiContainer.style.left = `${left}%`;
|
confettiContainer.style.left = `${left}%`;
|
||||||
confettiContainer.style.backgroundColor = color;
|
confettiContainer.style.backgroundColor = color;
|
||||||
|
confettiContainer.style.width = `${size}rem`;
|
||||||
|
confettiContainer.style.height = `${size * 0.6}rem`;
|
||||||
|
confettiContainer.style.transform = `rotate(${rotation}deg)`;
|
||||||
|
confettiContainer.style.animation = `confetti-fall ${duration}s linear forwards`;
|
||||||
|
|
||||||
document.querySelector('.results-screen').appendChild(confettiContainer);
|
document.querySelector('.results-screen').appendChild(confettiContainer);
|
||||||
|
|
||||||
// Remove after animation
|
// Nach Animation entfernen
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
confettiContainer.remove();
|
confettiContainer.remove();
|
||||||
}, 5000);
|
}, duration * 1000);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create multiple confetti pieces
|
// Mehrere Konfetti-Stücke erstellen
|
||||||
const confettiInterval = setInterval(() => {
|
const confettiInterval = setInterval(() => {
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 5; i++) {
|
||||||
createConfetti();
|
createConfetti();
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 200);
|
||||||
|
|
||||||
// Stop confetti after some time
|
// Konfetti nach einiger Zeit stoppen
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
clearInterval(confettiInterval);
|
clearInterval(confettiInterval);
|
||||||
setConfetti(false);
|
setConfetti(false);
|
||||||
@ -56,7 +63,7 @@ const ResultsScreen = () => {
|
|||||||
}
|
}
|
||||||
}, [confetti]);
|
}, [confetti]);
|
||||||
|
|
||||||
// Get YouTube video ID from link
|
// YouTube-Video-ID aus Link extrahieren
|
||||||
function getYouTubeId(url) {
|
function getYouTubeId(url) {
|
||||||
if (!url) return null;
|
if (!url) return null;
|
||||||
|
|
||||||
@ -69,7 +76,7 @@ const ResultsScreen = () => {
|
|||||||
if (!winner) {
|
if (!winner) {
|
||||||
return (
|
return (
|
||||||
<div className="results-screen">
|
<div className="results-screen">
|
||||||
<h2>Calculating results...</h2>
|
<h2>Ergebnisse werden berechnet...</h2>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -77,14 +84,14 @@ const ResultsScreen = () => {
|
|||||||
return (
|
return (
|
||||||
<div className="results-screen">
|
<div className="results-screen">
|
||||||
<header>
|
<header>
|
||||||
<h1><FontAwesomeIcon icon={faTrophy} /> Winner!</h1>
|
<h1><FontAwesomeIcon icon={faTrophy} /> Gewinner!</h1>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className="winner-card">
|
<div className="winner-card">
|
||||||
<div className="winner-info">
|
<div className="winner-info">
|
||||||
<h2>{winner.title}</h2>
|
<h2>{winner.title}</h2>
|
||||||
<h3>by {winner.artist}</h3>
|
<h3>von {winner.artist}</h3>
|
||||||
<p className="submitter">Submitted by: {winner.submittedByName}</p>
|
<p className="submitter">Eingereicht von: {winner.submittedByName}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{winnerVideoId ? (
|
{winnerVideoId ? (
|
||||||
@ -104,17 +111,17 @@ const ResultsScreen = () => {
|
|||||||
onClick={() => setShowBattleHistory(!showBattleHistory)}
|
onClick={() => setShowBattleHistory(!showBattleHistory)}
|
||||||
>
|
>
|
||||||
<FontAwesomeIcon icon={faChartLine} />
|
<FontAwesomeIcon icon={faChartLine} />
|
||||||
{showBattleHistory ? 'Hide Battle History' : 'Show Battle History'}
|
{showBattleHistory ? 'Kampfverlauf ausblenden' : 'Kampfverlauf anzeigen'}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button className="btn primary" onClick={leaveLobby}>
|
<button className="btn primary" onClick={leaveLobby}>
|
||||||
<FontAwesomeIcon icon={faHome} /> Return to Home
|
<FontAwesomeIcon icon={faHome} /> Zurück zum Start
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showBattleHistory && (
|
{showBattleHistory && (
|
||||||
<div className="battle-history">
|
<div className="battle-history">
|
||||||
<h3>Battle History</h3>
|
<h3>Kampfverlauf</h3>
|
||||||
|
|
||||||
<div className="battles-list">
|
<div className="battles-list">
|
||||||
{lobby?.battles?.map((battle, index) => {
|
{lobby?.battles?.map((battle, index) => {
|
||||||
@ -122,13 +129,13 @@ const ResultsScreen = () => {
|
|||||||
const song1VideoId = getYouTubeId(battle.song1?.youtubeLink);
|
const song1VideoId = getYouTubeId(battle.song1?.youtubeLink);
|
||||||
const song2VideoId = battle.song2 ? getYouTubeId(battle.song2.youtubeLink) : null;
|
const song2VideoId = battle.song2 ? getYouTubeId(battle.song2.youtubeLink) : null;
|
||||||
|
|
||||||
// Handle bye rounds
|
// Freilos-Runden behandeln
|
||||||
const isByeRound = battle.bye === true || !battle.song2;
|
const isByeRound = battle.bye === true || !battle.song2;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={index} className="battle-item">
|
<div key={index} className="battle-item">
|
||||||
<div className="battle-header">
|
<div className="battle-header">
|
||||||
<h4>Round {battle.round + 1}, Battle {index + 1} {isByeRound ? "(Automatic Advance)" : ""}</h4>
|
<h4>Runde {battle.round + 1}, Kampf {index + 1} {isByeRound ? "(Automatisches Weiterkommen)" : ""}</h4>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="battle-songs">
|
<div className="battle-songs">
|
||||||
@ -136,19 +143,28 @@ const ResultsScreen = () => {
|
|||||||
<div className="song-info">
|
<div className="song-info">
|
||||||
<h5>{battle.song1.title}</h5>
|
<h5>{battle.song1.title}</h5>
|
||||||
<p>{battle.song1.artist}</p>
|
<p>{battle.song1.artist}</p>
|
||||||
<span className="votes">{battle.song1Votes} votes</span>
|
<span className="votes">{battle.song1Votes} Stimmen</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="versus">VS</div>
|
<div className="versus">{isByeRound ? 'FREILOS' : 'VS'}</div>
|
||||||
|
|
||||||
<div className={`battle-song ${!isWinner ? 'winner' : ''}`}>
|
{battle.song2 ? (
|
||||||
<div className="song-info">
|
<div className={`battle-song ${!isWinner ? 'winner' : ''}`}>
|
||||||
<h5>{battle.song2.title}</h5>
|
<div className="song-info">
|
||||||
<p>{battle.song2.artist}</p>
|
<h5>{battle.song2.title}</h5>
|
||||||
<span className="votes">{battle.song2Votes} votes</span>
|
<p>{battle.song2.artist}</p>
|
||||||
|
<span className="votes">{battle.song2Votes} Stimmen</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
) : (
|
||||||
|
<div className="battle-song bye">
|
||||||
|
<div className="song-info">
|
||||||
|
<h5>Automatisches Freilos</h5>
|
||||||
|
<p>Kein Gegner</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -10,6 +10,7 @@ function VotingScreen() {
|
|||||||
const [hasVoted, setHasVoted] = useState(false);
|
const [hasVoted, setHasVoted] = useState(false);
|
||||||
const [selectedSong, setSelectedSong] = useState(null);
|
const [selectedSong, setSelectedSong] = useState(null);
|
||||||
const [countdown, setCountdown] = useState(null);
|
const [countdown, setCountdown] = useState(null);
|
||||||
|
const [processingByeAdvance, setProcessingByeAdvance] = useState(false);
|
||||||
|
|
||||||
// Hole aktuellen Kampf
|
// Hole aktuellen Kampf
|
||||||
const battle = lobby?.currentBattle || null;
|
const battle = lobby?.currentBattle || null;
|
||||||
@ -62,6 +63,24 @@ function VotingScreen() {
|
|||||||
// setHasVoted wird jetzt durch den useEffect behandelt, der die Stimmen prüft
|
// setHasVoted wird jetzt durch den useEffect behandelt, der die Stimmen prüft
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Handle bye round advancement - für automatisches Weiterkommen
|
||||||
|
const handleByeAdvance = async () => {
|
||||||
|
if (processingByeAdvance) return;
|
||||||
|
|
||||||
|
setProcessingByeAdvance(true);
|
||||||
|
try {
|
||||||
|
// Alle Spieler im Bye-Modus "stimmen" automatisch für das einzige Lied
|
||||||
|
if (battle && battle.song1 && !battle.song2 && battle.song1.id) {
|
||||||
|
await submitVote(battle.song1.id);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Fehler beim Fortfahren:", error);
|
||||||
|
} finally {
|
||||||
|
// Verzögerung, um mehrere Klicks zu verhindern
|
||||||
|
setTimeout(() => setProcessingByeAdvance(false), 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Hole YouTube-Video-IDs aus Links
|
// Hole YouTube-Video-IDs aus Links
|
||||||
const getYouTubeId = (url) => {
|
const getYouTubeId = (url) => {
|
||||||
if (!url) return null;
|
if (!url) return null;
|
||||||
@ -120,8 +139,12 @@ function VotingScreen() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="voting-status">
|
<div className="voting-status">
|
||||||
<button className="btn primary pixelated full-width" onClick={() => submitVote(battle.song1.id)}>
|
<button
|
||||||
Weiter zum nächsten Kampf
|
className={`btn primary pixelated full-width ${processingByeAdvance ? 'disabled' : ''}`}
|
||||||
|
onClick={handleByeAdvance}
|
||||||
|
disabled={processingByeAdvance}
|
||||||
|
>
|
||||||
|
{processingByeAdvance ? 'Wird geladen...' : 'Mach weiter'}
|
||||||
<span className="pixel-corner tl"></span>
|
<span className="pixel-corner tl"></span>
|
||||||
<span className="pixel-corner tr"></span>
|
<span className="pixel-corner tr"></span>
|
||||||
<span className="pixel-corner bl"></span>
|
<span className="pixel-corner bl"></span>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user