Skip to content

Commit 5d778cc

Browse files
committed
Added Highscores and Fixed layouts
1 parent a7a4e05 commit 5d778cc

File tree

11 files changed

+114
-22
lines changed

11 files changed

+114
-22
lines changed

index.html

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,25 @@
5151
if ('serviceWorker' in navigator) {
5252
window.addEventListener('load', async () => {
5353
try {
54-
// Unregister any existing service workers first
55-
const registrations = await navigator.serviceWorker.getRegistrations();
56-
for (let registration of registrations) {
57-
await registration.unregister();
58-
}
59-
6054
const registration = await navigator.serviceWorker.register('/sw.js');
61-
console.log('ServiceWorker registration successful');
55+
56+
// Add message listener for updates
57+
navigator.serviceWorker.addEventListener('message', (event) => {
58+
if (event.data && event.data.type === 'APP_UPDATED' && event.data.forceReload) {
59+
// Clear cache and reload
60+
caches.keys().then((keyList) => {
61+
return Promise.all(keyList.map((key) => caches.delete(key)));
62+
}).then(() => {
63+
window.location.reload(true);
64+
});
65+
}
66+
});
67+
68+
// Check for updates every 30 minutes
69+
setInterval(() => {
70+
registration.update();
71+
}, 1800000);
72+
6273
} catch (err) {
6374
console.log('ServiceWorker registration failed: ', err);
6475
}

public/assets/translations/ui/de.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"accuracy": "Genauigkeit",
1515
"restart": "Neu starten",
1616
"roundComplete": "Runde beendet!",
17+
"newHighscore": "Neuer Highscore!",
1718
"finalScore": "Endpunktzahl",
1819
"startNewRound": "Runde starten",
1920
"replayIncorrect": "Falsche wiederholen",

public/assets/translations/ui/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"accuracy": "Accuracy",
1515
"restart": "Restart",
1616
"roundComplete": "Round Complete!",
17+
"newHighscore": "New Highscore!",
1718
"finalScore": "Final Score",
1819
"startNewRound": "New Round",
1920
"replayIncorrect": "Replay incorrect",

public/assets/translations/ui/es.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"accuracy": "Precisión",
1515
"restart": "Reiniciar",
1616
"roundComplete": "¡Ronda completa!",
17+
"newHighscore": "¡Nuevo récord!",
1718
"finalScore": "Puntuación final",
1819
"startNewRound": "Nueva ronda",
1920
"replayIncorrect": "Repetir incorrectas",

public/assets/translations/ui/ru.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"accuracy": "Точность",
1515
"restart": "Начать заново",
1616
"roundComplete": "Раунд завершен!",
17+
"newHighscore": "Новый рекорд!",
1718
"finalScore": "Итоговый счет",
1819
"startNewRound": "Новый раунд",
1920
"replayIncorrect": "Повторить неверные",

public/sw.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const VERSION = '1.1.5';
1+
const VERSION = '1.2.5';
22
const CACHE_NAME = `flag-trainer-v${VERSION}`;
33
const BASE_PATH = '/';
44

@@ -113,7 +113,8 @@ self.addEventListener('activate', (event) => {
113113
clients.forEach(client => {
114114
client.postMessage({
115115
type: 'APP_UPDATED',
116-
version: VERSION
116+
version: VERSION,
117+
forceReload: true
117118
});
118119
});
119120
});

src/components/Game/GameOver.css

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
.game-over {
22
position: fixed;
3-
top: 50%;
3+
top: calc(50% + 38px);
44
left: 50%;
55
transform: translate(-50%, -50%);
66
background-color: var(--color-surface);
@@ -9,7 +9,7 @@
99
border-radius: 12px;
1010
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
1111
text-align: center;
12-
max-height: 90vh;
12+
max-height: calc(90vh - 76px);
1313
width: 300px;
1414
max-width: 500px;
1515
animation: fadeIn 0.3s ease-out;
@@ -85,7 +85,7 @@
8585
}
8686

8787
.game-over h2 {
88-
margin-bottom: 2rem;
88+
margin: 0 0 1rem 0;
8989
color: var(--color-text);
9090
font-size: 2rem;
9191
font-weight: 600;
@@ -94,9 +94,8 @@
9494
.stats {
9595
display: flex;
9696
flex-direction: column;
97-
gap: 1.5rem;
98-
margin-bottom: 3rem;
99-
padding-right: 5px;
97+
gap: 1rem;
98+
margin-bottom: 1rem;
10099
}
101100

102101
.stat-item {
@@ -118,6 +117,16 @@
118117
font-weight: 600;
119118
}
120119

120+
.stat-item .with-icon {
121+
display: flex;
122+
align-items: center;
123+
gap: 0.5rem;
124+
}
125+
126+
.stat-item .material-symbols-outlined {
127+
font-size: 1.2rem;
128+
}
129+
121130
@keyframes fadeIn {
122131
from {
123132
opacity: 0;

src/components/Game/GameOver.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ export const GameOver: React.FC<{
99
}> = observer(({ onRestart }) => {
1010
const { gameStore, settingsStore } = useStores();
1111
const roundCompleteText = useTranslation('roundComplete', settingsStore.language, true);
12+
const newHighscoreText = useTranslation('newHighscore', settingsStore.language, true);
1213
const finalScoreText = useTranslation('finalScore', settingsStore.language, true);
1314
const correctAnswersText = useTranslation('correctAnswers', settingsStore.language, true);
1415
const timeText = useTranslation('time', settingsStore.language, true);
1516
const startNewRoundText = useTranslation('startNewRound', settingsStore.language, true);
1617
const replayIncorrectText = useTranslation('replayIncorrect', settingsStore.language, true);
1718
const difficultyText = useTranslation('difficulty', settingsStore.language, true);
1819
const difficultyLevelText = useTranslation(settingsStore.difficulty, settingsStore.language, true);
20+
const gameModeText = useTranslation('gameMode', settingsStore.language, true);
1921

2022
const handleReplayIncorrect = () => {
2123
gameStore.replayIncorrect();
@@ -25,7 +27,7 @@ export const GameOver: React.FC<{
2527

2628
return (
2729
<div className="game-over">
28-
<h2>{roundCompleteText}</h2>
30+
<h2>{gameStore.isNewHighscore ? newHighscoreText : roundCompleteText}</h2>
2931
<div className="stats">
3032
<div className="stat-item">
3133
<label>{finalScoreText}:</label>
@@ -43,6 +45,16 @@ export const GameOver: React.FC<{
4345
<label>{difficultyText}:</label>
4446
<span>{difficultyLevelText}</span>
4547
</div>
48+
<div className="stat-item">
49+
<label>{gameModeText}:</label>
50+
<span className="with-icon">
51+
<span className="material-symbols-outlined">
52+
{settingsStore.gameMode === 'quiz' ? 'quiz' :
53+
settingsStore.gameMode === 'type' ? 'keyboard' : 'flag'}
54+
</span>
55+
{useTranslation(settingsStore.gameMode, settingsStore.language, true)}
56+
</span>
57+
</div>
4658
</div>
4759
<div className={`game-over-buttons ${!hasIncorrectFlags ? 'single-button' : ''}`}>
4860
<button className="restart-button" onClick={onRestart}>

src/components/Header/Header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export const Header: React.FC<HeaderProps> = observer(({ onSettingsClick, showCo
7171
settings
7272
</button>
7373

74-
<h1>Test Update</h1>
74+
<h1>Flag Trainer</h1>
7575

7676
{isVisible && shouldShowScore && (
7777
<div className={`score-container ${!showControls ? 'hidden' : ''}`}>

src/services/StorageService.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ interface SettingsState {
3636
export class StorageService {
3737
private static GAME_STATE_KEY = 'flagTrainer_gameState';
3838
private static SETTINGS_STATE_KEY = 'flagTrainer_settingsState';
39+
private static HIGHSCORES_KEY = 'flagTrainer_highscores';
3940

4041
static saveGameState(state: GameState): void {
4142
localStorage.setItem(this.GAME_STATE_KEY, JSON.stringify(state));
@@ -63,4 +64,27 @@ export class StorageService {
6364
static clearGameState(): void {
6465
localStorage.removeItem(this.GAME_STATE_KEY);
6566
}
67+
68+
static getHighscore(regions: Region[], gameMode: GameMode, difficulty: Difficulty): number | null {
69+
const highscores = this.getHighscores();
70+
const key = this.createHighscoreKey(regions, gameMode, difficulty);
71+
return highscores[key] || null;
72+
}
73+
74+
static setHighscore(regions: Region[], gameMode: GameMode, difficulty: Difficulty, score: number): void {
75+
const highscores = this.getHighscores();
76+
const key = this.createHighscoreKey(regions, gameMode, difficulty);
77+
highscores[key] = score;
78+
localStorage.setItem(this.HIGHSCORES_KEY, JSON.stringify(highscores));
79+
}
80+
81+
private static getHighscores(): Record<string, number> {
82+
const stored = localStorage.getItem(this.HIGHSCORES_KEY);
83+
return stored ? JSON.parse(stored) : {};
84+
}
85+
86+
private static createHighscoreKey(regions: Region[], gameMode: GameMode, difficulty: Difficulty): string {
87+
const sortedRegions = [...regions].sort().join(',');
88+
return `${sortedRegions}|${gameMode}|${difficulty}`;
89+
}
6690
}

0 commit comments

Comments
 (0)