diff --git a/ToDo.md b/ToDo.md index f2a0783..088b5ec 100644 --- a/ToDo.md +++ b/ToDo.md @@ -1,9 +1,9 @@ [ ] Improove: In the preview page, add full header. -[ ] Improove: Split header into two parts: static top and dynamic bottom. [ ] Improove: In the preview page, add possibility select custom color for each target. -[ ] Strategy: Add quiz game to guess the logo or flags and invert Done: +[v] Strategy: Add quiz game to guess the logo or flags and invert +[v] Improove: Split header into two parts: static top and dynamic bottom. [v] Improove: To the tiny card add the color chooser. It should be one circle that display current color, on click it should open color picker. [v] Strategy: Add differents base/collections of images: Flags, [v] Strategy: WebApp, PWA diff --git a/public/icons/danger-triangle.svg b/public/icons/danger-triangle.svg new file mode 100644 index 0000000..cafb954 --- /dev/null +++ b/public/icons/danger-triangle.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/components/QuizSettings.svelte b/src/components/QuizSettings.svelte new file mode 100644 index 0000000..7cd442d --- /dev/null +++ b/src/components/QuizSettings.svelte @@ -0,0 +1,374 @@ + + +{#if showSettings} +
+ +
+{/if} + +{#if showResetConfirmation} + +{/if} + + diff --git a/src/pages/FlagQuiz.svelte b/src/pages/FlagQuiz.svelte index c6d278e..fc33fa9 100644 --- a/src/pages/FlagQuiz.svelte +++ b/src/pages/FlagQuiz.svelte @@ -4,7 +4,7 @@ import Footer from '../components/Footer.svelte'; import InlineSvg from '../components/InlineSvg.svelte'; import Achievements from '../components/Achievements.svelte'; - import AchievementButton from '../components/AchievementButton.svelte'; + import QuizSettings from '../components/QuizSettings.svelte'; // Game data let flags = []; @@ -27,6 +27,12 @@ let showCountryInfo = false; let showResultCountryInfo = false; + // Auto-advance timer variables + let autoAdvanceTimer = null; + let timerProgress = 0; + let timerDuration = 2000; // 2 seconds + let timerStartTime = 0; + // Scoring let score = { correct: 0, total: 0, skipped: 0 }; let gameStats = { correct: 0, wrong: 0, total: 0, skipped: 0 }; @@ -197,6 +203,9 @@ showCountryInfo = false; showResultCountryInfo = false; + // Cancel any active auto-advance timer + cancelAutoAdvanceTimer(); + // Randomly choose question type questionType = Math.random() < 0.5 ? 'flag-to-country' : 'country-to-flag'; @@ -348,9 +357,7 @@ // Auto-advance to next question with different delays if auto mode is on if (autoAdvance) { const delay = isCorrect ? 2000 : 4000; // Double delay for wrong answers - setTimeout(() => { - generateQuestion(); - }, delay); + startAutoAdvanceTimer(delay); } } function skipQuestion() { if (gameState !== 'question') return; @@ -377,6 +384,38 @@ generateQuestion(); } + function startAutoAdvanceTimer(duration) { + timerDuration = duration; + timerProgress = 0; + timerStartTime = Date.now(); + + // Clear any existing timer + if (autoAdvanceTimer) { + clearInterval(autoAdvanceTimer); + } + + // Update progress every 50ms for smooth animation + autoAdvanceTimer = setInterval(() => { + const elapsed = Date.now() - timerStartTime; + timerProgress = Math.min((elapsed / duration) * 100, 100); + + if (timerProgress >= 100) { + clearInterval(autoAdvanceTimer); + autoAdvanceTimer = null; + timerProgress = 0; + generateQuestion(); + } + }, 50); + } + + function cancelAutoAdvanceTimer() { + if (autoAdvanceTimer) { + clearInterval(autoAdvanceTimer); + autoAdvanceTimer = null; + timerProgress = 0; + } + } + function resetGame() { score = { correct: 0, total: 0, skipped: 0 }; generateQuestion(); @@ -396,23 +435,23 @@ showSettings = !showSettings; } - function handleOverlayClick(event) { - if (event.target === event.currentTarget) { - toggleSettings(); - } + function handleSettingsChange(event) { + const { autoAdvance: newAutoAdvance, focusWrongAnswers: newFocusWrong, reduceCorrectAnswers: newReduceCorrect, soundEnabled: newSoundEnabled } = event.detail; + autoAdvance = newAutoAdvance; + focusWrongAnswers = newFocusWrong; + reduceCorrectAnswers = newReduceCorrect; + soundEnabled = newSoundEnabled; } - function handleOverlayKeydown(event) { - if (event.key === 'Escape') { - toggleSettings(); - } + function handleSettingsToggle(event) { + showSettings = event.detail; } - function resetAllStats() { - showResetConfirmation = true; + function handleResetConfirmation(event) { + showResetConfirmation = event.detail; } - function confirmReset() { + function handleResetStats() { // Reset game statistics gameStats = { correct: 0, wrong: 0, total: 0, skipped: 0 }; score = { correct: 0, total: 0, skipped: 0 }; @@ -427,16 +466,12 @@ correctAnswers = new Map(); localStorage.removeItem('flagQuizCorrectAnswers'); - // Reset achievements + // Reset achievements if component is available if (achievementsComponent) { - localStorage.removeItem('flagQuizAchievements'); - // Reinitialize achievements component - achievementsComponent.loadAchievements(); - updateAchievementCount(); + achievementsComponent.resetAllAchievements(); } showResetConfirmation = false; - showSettings = false; } function cancelReset() { @@ -535,115 +570,21 @@
- - {#if showSettings} -
- -
- {/if} - - - {#if showResetConfirmation} -
e.target === e.currentTarget && cancelReset()} - on:keydown={(e) => { - if (e.key === 'Escape' || e.key === 'Enter' || e.key === ' ') { - cancelReset(); - } - }} - > -
-
-

⚠️ Reset All Data

-
-
-

This action will permanently delete:

-
    -
  • ✗ All game statistics (correct, wrong, skipped answers)
  • -
  • ✗ Current session score
  • -
  • ✗ All unlocked achievements
  • -
  • ✗ Achievement progress
  • -
  • ✗ Wrong answer tracking data
  • -
-

This cannot be undone!

-
-
- - -
-
-
- {/if} + + Next Question → {/if} + + + {#if autoAdvance && gameState === 'answered' && timerProgress > 0} +
+
+
+
+ Next question in {Math.ceil((timerDuration - (timerProgress / 100 * timerDuration)) / 1000)}s +
+ {/if}
{/if} @@ -1353,6 +1304,35 @@ background: var(--color-text-primary); } + /* Auto-advance timer styles */ + .auto-advance-timer { + margin-top: 1rem; + text-align: center; + } + + .timer-bar { + width: 100%; + height: 6px; + background: var(--color-bg-tertiary); + border-radius: 3px; + overflow: hidden; + margin-bottom: 0.5rem; + border: 1px solid var(--color-border); + } + + .timer-progress { + height: 100%; + background: var(--color-primary); + transition: width 0.05s linear; + border-radius: 2px; + } + + .timer-text { + font-size: 0.8rem; + color: var(--color-text-secondary); + font-weight: 500; + } + @media (max-width: 768px) { .container { padding: 0.75rem;