diff --git a/src/App.svelte b/src/App.svelte index 5ed7ed9..2a582c0 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -1,6 +1,7 @@ @@ -805,7 +709,7 @@
@@ -839,9 +742,9 @@ {gameStats} {currentStreak} show={showAchievements} - capitalsCorrect={capitalsCorrect} - perfectRounds={perfectRounds} - mapChallengeCompleted={mapChallengeCompleted} + {capitalsCorrect} + {perfectRounds} + {mapChallengeCompleted} on:close={() => (showAchievements = false)} on:achievementsUnlocked={handleAchievementsUnlocked} /> @@ -853,7 +756,7 @@ {sessionStats} {sessionLength} {showSessionResults} - quizInfo={quizInfo} + {quizInfo} on:startQuiz={startNewSession} on:openSettings={() => (showSettings = true)} on:closeResults={() => (showSessionResults = false)} @@ -900,8 +803,11 @@ + {:else if (!autoAdvance && gameState === "answered") || (autoAdvance && gameState === "answered" && sessionRestoredFromReload)} - + {/if} diff --git a/src/pages/FlagQuiz.svelte b/src/pages/FlagQuiz.svelte index b9a7849..f711608 100644 --- a/src/pages/FlagQuiz.svelte +++ b/src/pages/FlagQuiz.svelte @@ -1,7 +1,21 @@ @@ -915,7 +701,7 @@ import { quizInfo } from '../quizInfo/FlagQuizInfo.js';
@@ -955,12 +740,12 @@ import { quizInfo } from '../quizInfo/FlagQuizInfo.js'; {#if quizSubpage === "welcome"} - (showSettings = true)} on:closeResults={() => (showSessionResults = false)} diff --git a/src/pages/Game.svelte b/src/pages/Game.svelte index 12e5f64..2f7e79f 100644 --- a/src/pages/Game.svelte +++ b/src/pages/Game.svelte @@ -1,4 +1,5 @@ -
+
diff --git a/src/quizLogic/advanceTimer.js b/src/quizLogic/advanceTimer.js new file mode 100644 index 0000000..772d7fa --- /dev/null +++ b/src/quizLogic/advanceTimer.js @@ -0,0 +1,56 @@ +// Shared advance timer for quizzes +// createAdvanceTimer(onProgress, onComplete) -> { start(duration), cancel() } +export function createAdvanceTimer(onProgress, onComplete) { + let timer = null; + let startTime = 0; + let duration = 0; + + function _clear() { + if (timer) { + clearInterval(timer); + timer = null; + } + } + + return { + start(d) { + duration = d || 0; + // reset progress immediately + try { + onProgress(0); + } catch (e) { + // ignore + } + startTime = Date.now(); + _clear(); + timer = setInterval(() => { + const elapsed = Date.now() - startTime; + const progress = Math.min((elapsed / duration) * 100, 100); + try { + onProgress(progress); + } catch (e) { + // ignore + } + if (progress >= 100) { + _clear(); + try { + onProgress(0); + } catch (e) {} + if (typeof onComplete === 'function') { + try { + onComplete(); + } catch (e) { + console.error('advanceTimer onComplete error', e); + } + } + } + }, 50); + }, + cancel() { + _clear(); + try { + onProgress(0); + } catch (e) {} + }, + }; +} diff --git a/src/quizLogic/quizSession.js b/src/quizLogic/quizSession.js index 604bd30..9b984f2 100644 --- a/src/quizLogic/quizSession.js +++ b/src/quizLogic/quizSession.js @@ -21,3 +21,22 @@ export function loadSessionState(key, defaultState) { export function clearSessionState(key) { localStorage.removeItem(key); } + +// Return a new session state object for a quiz with the provided session length +export function createNewSessionState(sessionLength = 10) { + return { + score: { correct: 0, total: 0, skipped: 0 }, + currentSessionQuestions: 0, + sessionStats: { + correct: 0, + wrong: 0, + skipped: 0, + total: 0, + sessionLength: sessionLength, + }, + sessionInProgress: true, + sessionStartTime: Date.now(), + showSessionResults: false, + sessionRestoredFromReload: false, + }; +} diff --git a/src/utils/theme.js b/src/utils/theme.js new file mode 100644 index 0000000..1cbd1ea --- /dev/null +++ b/src/utils/theme.js @@ -0,0 +1,29 @@ +export function applyTheme(theme) { + let effectiveTheme = theme; + if (theme === "system") { + effectiveTheme = window.matchMedia("(prefers-color-scheme: dark)").matches + ? "dark" + : "light"; + } + document.documentElement.setAttribute("data-theme", effectiveTheme); + document.documentElement.className = effectiveTheme; + console.log("[Theme] Applied theme:", effectiveTheme); +} + +export function setTheme(newTheme) { + if (newTheme === "light" || newTheme === "dark" || newTheme === "system") { + // Persist choice and apply immediately + localStorage.setItem("theme", newTheme); + console.log("[Theme] setTheme:", newTheme); + setTimeout(() => applyTheme(newTheme), 0); + return newTheme; + } + } + +export function getStoredTheme(defaultTheme = "system") { + return (typeof localStorage !== "undefined" && localStorage.getItem("theme")) || defaultTheme; +} + +// Svelte store for reactive theme across components +import { writable } from 'svelte/store'; +export const themeStore = writable(getStoredTheme());