mirror of
https://github.com/shadoll/sLogos.git
synced 2025-12-20 03:26:59 +00:00
Refactor session management in quizzes to use centralized restoreSessionState function and improve flag loading logic with shared utilities
This commit is contained in:
@@ -9,14 +9,15 @@
|
||||
} from "../quizLogic/quizGlobalStats.js";
|
||||
import {
|
||||
saveSessionState,
|
||||
loadSessionState,
|
||||
clearSessionState,
|
||||
createNewSessionState,
|
||||
restoreSessionState,
|
||||
} from "../quizLogic/quizSession.js";
|
||||
import { createAdvanceTimer } from "../quizLogic/advanceTimer.js";
|
||||
import { playCorrectSound, playWrongSound } from "../quizLogic/quizSound.js";
|
||||
import { quizInfo } from "../quizInfo/CapitalsQuizInfo.js";
|
||||
import { onMount } from "svelte";
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import { loadFlags as loadFlagsShared, getCountryName, getFlagImage, pickWeightedFlag } from "../quizLogic/flags.js";
|
||||
import Header from "../components/Header.svelte";
|
||||
import Footer from "../components/Footer.svelte";
|
||||
import Achievements from "../components/Achievements.svelte";
|
||||
@@ -36,10 +37,7 @@
|
||||
let quizSubpage = "welcome"; // 'welcome' or 'quiz'
|
||||
let selectedAnswer = null;
|
||||
let answered = false;
|
||||
let isAnswered = false;
|
||||
let resultMessage = "";
|
||||
let showResult = false;
|
||||
let timeoutId = null;
|
||||
let showCountryInfo = false;
|
||||
let showResultCountryInfo = false;
|
||||
|
||||
@@ -56,8 +54,8 @@
|
||||
// Scoring
|
||||
let score = { correct: 0, total: 0, skipped: 0 };
|
||||
let gameStats = { correct: 0, wrong: 0, total: 0, skipped: 0 };
|
||||
let wrongAnswers = new Map(); // Track flags answered incorrectly: flag.name -> count
|
||||
let correctAnswers = new Map(); // Track flags answered correctly: flag.name -> count
|
||||
let wrongAnswers = new Map();
|
||||
let correctAnswers = new Map();
|
||||
|
||||
// Achievement System
|
||||
let currentStreak = 0;
|
||||
@@ -90,7 +88,7 @@
|
||||
let showSessionResults = false;
|
||||
let sessionInProgress = false;
|
||||
let sessionStartTime = null;
|
||||
let sessionRestoredFromReload = false; // Track if session was restored from page reload
|
||||
let sessionRestoredFromReload = false;
|
||||
|
||||
// Update achievement count when achievements component is available
|
||||
$: if (achievementsComponent) {
|
||||
@@ -188,76 +186,39 @@
|
||||
await loadFlags();
|
||||
settingsLoaded = true;
|
||||
|
||||
// Load or initialize session
|
||||
const loadedSession = loadSessionState("capitalsQuizSessionState", null);
|
||||
if (loadedSession) {
|
||||
// Restore session
|
||||
sessionInProgress = loadedSession.sessionInProgress;
|
||||
currentSessionQuestions = loadedSession.currentSessionQuestions || 0;
|
||||
sessionStats = loadedSession.sessionStats || {
|
||||
correct: 0,
|
||||
wrong: 0,
|
||||
skipped: 0,
|
||||
total: 0,
|
||||
sessionLength,
|
||||
};
|
||||
score = loadedSession.score || { correct: 0, total: 0, skipped: 0 };
|
||||
currentQuestion = loadedSession.currentQuestion;
|
||||
selectedAnswer = loadedSession.selectedAnswer;
|
||||
showResult = loadedSession.showResult || false;
|
||||
gameState = loadedSession.gameState || "question";
|
||||
quizSubpage = "quiz";
|
||||
sessionStartTime = loadedSession.sessionStartTime;
|
||||
questionKey = loadedSession.questionKey || 0;
|
||||
// Load or initialize session (centralized)
|
||||
const restored = restoreSessionState("capitalsQuizSessionState");
|
||||
if (restored && restored.sessionInProgress) {
|
||||
sessionInProgress = restored.sessionInProgress;
|
||||
currentSessionQuestions = restored.currentSessionQuestions;
|
||||
sessionStats = restored.sessionStats;
|
||||
score = restored.score;
|
||||
currentQuestion = restored.currentQuestion;
|
||||
selectedAnswer = restored.selectedAnswer;
|
||||
showResult = restored.showResult;
|
||||
gameState = restored.gameState;
|
||||
quizSubpage = restored.quizSubpage;
|
||||
sessionStartTime = restored.sessionStartTime;
|
||||
questionKey = restored.questionKey || 0;
|
||||
sessionRestoredFromReload = restored.sessionRestoredFromReload;
|
||||
|
||||
// Mark that session was restored from reload
|
||||
sessionRestoredFromReload = true;
|
||||
|
||||
// If we don't have a current question, generate one
|
||||
if (!currentQuestion) {
|
||||
generateQuestion();
|
||||
}
|
||||
} else {
|
||||
// No saved state, show welcome page
|
||||
quizSubpage = "welcome";
|
||||
gameState = "welcome";
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup on component destroy: cancel any running advance timer
|
||||
onDestroy(() => {
|
||||
if (advanceTimer) advanceTimer.cancel();
|
||||
});
|
||||
|
||||
async function loadFlags() {
|
||||
try {
|
||||
const response = await fetch("/data/flags.json");
|
||||
const data = await response.json();
|
||||
// Filter for only country flags (has "Country" tag) and ensure we have country name and capital
|
||||
flags = data.filter(
|
||||
(flag) =>
|
||||
!flag.disable &&
|
||||
flag.meta?.country &&
|
||||
flag.meta?.capital &&
|
||||
flag.tags &&
|
||||
flag.tags.includes("Country"),
|
||||
);
|
||||
|
||||
// Remove duplicates based on country name
|
||||
const uniqueFlags = [];
|
||||
const seenCountries = new Set();
|
||||
|
||||
for (const flag of flags) {
|
||||
const countryName = flag.meta.country.toLowerCase().trim();
|
||||
if (!seenCountries.has(countryName)) {
|
||||
seenCountries.add(countryName);
|
||||
uniqueFlags.push(flag);
|
||||
}
|
||||
}
|
||||
|
||||
flags = uniqueFlags;
|
||||
console.log(
|
||||
`Loaded ${flags.length} unique country flags for capitals quiz`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("Error loading flags:", error);
|
||||
flags = [];
|
||||
}
|
||||
flags = await loadFlagsShared({ requireCapital: true });
|
||||
console.log(`Loaded ${flags.length} unique country flags for capitals quiz`);
|
||||
}
|
||||
|
||||
function generateQuestion() {
|
||||
@@ -297,48 +258,9 @@
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// Pick correct answer with adaptive learning settings
|
||||
let correctFlag;
|
||||
|
||||
// Simple fallback to avoid uninitialized variable errors
|
||||
if (settingsLoaded && (focusWrongAnswers || reduceCorrectAnswers)) {
|
||||
// Re-enable adaptive learning
|
||||
// Create weighted array based on learning settings
|
||||
const weightedFlags = [];
|
||||
for (const flag of flags) {
|
||||
const wrongCount = wrongAnswers.get(flag.name) || 0;
|
||||
const correctCount = correctAnswers.get(flag.name) || 0;
|
||||
|
||||
let weight = 1; // Base weight
|
||||
|
||||
// Increase weight for flags with wrong answers (if setting enabled)
|
||||
if (focusWrongAnswers && wrongCount > 0) {
|
||||
weight = Math.min(wrongCount + 1, 4); // Max 4x weight for wrong answers
|
||||
}
|
||||
|
||||
// Decrease weight for flags with correct answers (if setting enabled)
|
||||
if (reduceCorrectAnswers && correctCount > 0) {
|
||||
weight = weight / Math.min(correctCount + 1, 4); // Reduce weight, min 0.25x
|
||||
}
|
||||
|
||||
// Add flag to weighted array based on calculated weight
|
||||
const finalWeight = Math.max(0.25, weight); // Minimum weight to ensure variety
|
||||
const timesToAdd = Math.ceil(finalWeight);
|
||||
for (let i = 0; i < timesToAdd; i++) {
|
||||
weightedFlags.push(flag);
|
||||
}
|
||||
}
|
||||
|
||||
if (weightedFlags.length > 0) {
|
||||
correctFlag =
|
||||
weightedFlags[Math.floor(Math.random() * weightedFlags.length)];
|
||||
} else {
|
||||
correctFlag = flags[Math.floor(Math.random() * flags.length)];
|
||||
}
|
||||
} else {
|
||||
// Normal random selection
|
||||
correctFlag = flags[Math.floor(Math.random() * flags.length)];
|
||||
}
|
||||
// Pick correct answer using shared helper (handles adaptive weighting)
|
||||
const pick = pickWeightedFlag(flags, { focusWrongAnswers, reduceCorrectAnswers }, wrongAnswers, correctAnswers);
|
||||
const correctFlag = pick || flags[Math.floor(Math.random() * flags.length)];
|
||||
|
||||
const correctCapital = correctFlag.meta.capital.toLowerCase();
|
||||
|
||||
@@ -687,18 +609,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
function getCountryName(flag) {
|
||||
return flag.meta?.country || flag.name || "Unknown";
|
||||
}
|
||||
|
||||
// Use shared getCountryName/getFlagImage helpers from flags.js
|
||||
function getCapitalName(flag) {
|
||||
return flag.meta?.capital || "Unknown";
|
||||
}
|
||||
|
||||
function getFlagImage(flag) {
|
||||
return `/images/flags/${flag.path}`;
|
||||
}
|
||||
|
||||
function handleAchievementsUnlocked() {
|
||||
achievementCount = updateAchievementCount(achievementsComponent);
|
||||
}
|
||||
|
||||
@@ -9,14 +9,15 @@
|
||||
import { playCorrectSound, playWrongSound } from "../quizLogic/quizSound.js";
|
||||
import {
|
||||
saveSessionState,
|
||||
loadSessionState,
|
||||
clearSessionState,
|
||||
createNewSessionState,
|
||||
} from "../quizLogic/quizSession.js";
|
||||
import { restoreSessionState } from "../quizLogic/quizSession.js";
|
||||
import { createAdvanceTimer } from "../quizLogic/advanceTimer.js";
|
||||
|
||||
import { quizInfo } from "../quizInfo/FlagQuizInfo.js";
|
||||
import { onMount } from "svelte";
|
||||
import { onMount, onDestroy } from "svelte";
|
||||
import { loadFlags as loadFlagsShared, getCountryName, getFlagImage, pickWeightedFlag } from "../quizLogic/flags.js";
|
||||
import Header from "../components/Header.svelte";
|
||||
import Footer from "../components/Footer.svelte";
|
||||
import InlineSvg from "../components/InlineSvg.svelte";
|
||||
@@ -31,8 +32,6 @@
|
||||
let questionType = "flag-to-country"; // 'flag-to-country' or 'country-to-flag'
|
||||
|
||||
// Question and answer arrays
|
||||
let currentCountryOptions = [];
|
||||
let currentFlagOptions = [];
|
||||
let correctAnswer = "";
|
||||
|
||||
// Game states
|
||||
@@ -40,10 +39,7 @@
|
||||
let quizSubpage = "welcome"; // 'welcome' or 'quiz'
|
||||
let selectedAnswer = null;
|
||||
let answered = false;
|
||||
let isAnswered = false;
|
||||
let resultMessage = "";
|
||||
let showResult = false;
|
||||
let timeoutId = null;
|
||||
let showCountryInfo = false;
|
||||
let showResultCountryInfo = false;
|
||||
|
||||
@@ -58,8 +54,8 @@
|
||||
// Scoring
|
||||
let score = { correct: 0, total: 0, skipped: 0 };
|
||||
let gameStats = { correct: 0, wrong: 0, total: 0, skipped: 0 };
|
||||
let wrongAnswers = new Map(); // Track flags answered incorrectly: flag.name -> count
|
||||
let correctAnswers = new Map(); // Track flags answered correctly: flag.name -> count
|
||||
let wrongAnswers = new Map();
|
||||
let correctAnswers = new Map();
|
||||
|
||||
// Achievement System
|
||||
let currentStreak = 0;
|
||||
@@ -89,7 +85,7 @@
|
||||
let showSessionResults = false;
|
||||
let sessionInProgress = false;
|
||||
let sessionStartTime = null;
|
||||
let sessionRestoredFromReload = false; // Track if session was restored from page reload
|
||||
let sessionRestoredFromReload = false;
|
||||
|
||||
// Update achievement count when achievements component is available
|
||||
$: if (achievementsComponent) {
|
||||
@@ -193,36 +189,24 @@
|
||||
await loadFlags();
|
||||
settingsLoaded = true;
|
||||
|
||||
// Load or initialize session
|
||||
const loaded = loadSessionState("flagQuizSessionState", null);
|
||||
if (loaded) {
|
||||
if (loaded.sessionInProgress) {
|
||||
sessionInProgress = loaded.sessionInProgress;
|
||||
currentSessionQuestions = loaded.currentSessionQuestions || 0;
|
||||
sessionStats = loaded.sessionStats || {
|
||||
correct: 0,
|
||||
wrong: 0,
|
||||
skipped: 0,
|
||||
total: 0,
|
||||
sessionLength,
|
||||
};
|
||||
score = loaded.score || { correct: 0, total: 0, skipped: 0 };
|
||||
currentQuestion = loaded.currentQuestion;
|
||||
selectedAnswer = loaded.selectedAnswer;
|
||||
showResult = loaded.showResult || false;
|
||||
gameState = loaded.gameState || "question";
|
||||
quizSubpage = "quiz";
|
||||
sessionStartTime = loaded.sessionStartTime;
|
||||
questionKey = loaded.questionKey || 0;
|
||||
// Load or initialize session (centralized)
|
||||
const restored = restoreSessionState("flagQuizSessionState");
|
||||
if (restored && restored.sessionInProgress) {
|
||||
sessionInProgress = restored.sessionInProgress;
|
||||
currentSessionQuestions = restored.currentSessionQuestions;
|
||||
sessionStats = restored.sessionStats;
|
||||
score = restored.score;
|
||||
currentQuestion = restored.currentQuestion;
|
||||
selectedAnswer = restored.selectedAnswer;
|
||||
showResult = restored.showResult;
|
||||
gameState = restored.gameState;
|
||||
quizSubpage = restored.quizSubpage;
|
||||
sessionStartTime = restored.sessionStartTime;
|
||||
questionKey = restored.questionKey || 0;
|
||||
sessionRestoredFromReload = restored.sessionRestoredFromReload;
|
||||
|
||||
sessionRestoredFromReload = true;
|
||||
|
||||
if (!currentQuestion) {
|
||||
generateQuestion();
|
||||
}
|
||||
} else {
|
||||
quizSubpage = "welcome";
|
||||
gameState = "welcome";
|
||||
if (!currentQuestion) {
|
||||
generateQuestion();
|
||||
}
|
||||
} else {
|
||||
quizSubpage = "welcome";
|
||||
@@ -230,39 +214,16 @@
|
||||
}
|
||||
});
|
||||
|
||||
// Cleanup on component destroy: cancel any running advance timer
|
||||
onDestroy(() => {
|
||||
if (advanceTimer) advanceTimer.cancel();
|
||||
});
|
||||
|
||||
// use shared applyTheme from ../utils/theme.js
|
||||
|
||||
async function loadFlags() {
|
||||
try {
|
||||
const response = await fetch("/data/flags.json");
|
||||
const data = await response.json();
|
||||
// Filter for only country flags (has "Country" tag) and ensure we have country name
|
||||
flags = data.filter(
|
||||
(flag) =>
|
||||
!flag.disable &&
|
||||
flag.meta?.country &&
|
||||
flag.tags &&
|
||||
flag.tags.includes("Country"),
|
||||
);
|
||||
|
||||
// Remove duplicates based on country name
|
||||
const uniqueFlags = [];
|
||||
const seenCountries = new Set();
|
||||
|
||||
for (const flag of flags) {
|
||||
const countryName = flag.meta.country.toLowerCase().trim();
|
||||
if (!seenCountries.has(countryName)) {
|
||||
seenCountries.add(countryName);
|
||||
uniqueFlags.push(flag);
|
||||
}
|
||||
}
|
||||
|
||||
flags = uniqueFlags;
|
||||
console.log(`Loaded ${flags.length} unique country flags for quiz`);
|
||||
} catch (error) {
|
||||
console.error("Error loading flags:", error);
|
||||
flags = [];
|
||||
}
|
||||
flags = await loadFlagsShared();
|
||||
console.log(`Loaded ${flags.length} unique country flags for quiz`);
|
||||
}
|
||||
|
||||
function generateQuestion() {
|
||||
@@ -305,50 +266,11 @@
|
||||
// Randomly choose question type
|
||||
questionType = Math.random() < 0.5 ? "flag-to-country" : "country-to-flag";
|
||||
|
||||
// Pick correct answer with adaptive learning settings
|
||||
let correctFlag;
|
||||
// Pick correct answer with shared helper (handles adaptive settings)
|
||||
const pick = pickWeightedFlag(flags, { focusWrongAnswers, reduceCorrectAnswers }, wrongAnswers, correctAnswers);
|
||||
const correctFlag = pick || flags[Math.floor(Math.random() * flags.length)];
|
||||
|
||||
// Simple fallback to avoid uninitialized variable errors
|
||||
if (settingsLoaded && (focusWrongAnswers || reduceCorrectAnswers)) {
|
||||
// Re-enable adaptive learning
|
||||
// Create weighted array based on learning settings
|
||||
const weightedFlags = [];
|
||||
for (const flag of flags) {
|
||||
const wrongCount = wrongAnswers.get(flag.name) || 0;
|
||||
const correctCount = correctAnswers.get(flag.name) || 0;
|
||||
|
||||
let weight = 1; // Base weight
|
||||
|
||||
// Increase weight for flags with wrong answers (if setting enabled)
|
||||
if (focusWrongAnswers && wrongCount > 0) {
|
||||
weight = Math.min(wrongCount + 1, 4); // Max 4x weight for wrong answers
|
||||
}
|
||||
|
||||
// Decrease weight for flags with correct answers (if setting enabled)
|
||||
if (reduceCorrectAnswers && correctCount > 0) {
|
||||
weight = weight / Math.min(correctCount + 1, 4); // Reduce weight, min 0.25x
|
||||
}
|
||||
|
||||
// Add flag to weighted array based on calculated weight
|
||||
const finalWeight = Math.max(0.25, weight); // Minimum weight to ensure variety
|
||||
const timesToAdd = Math.ceil(finalWeight);
|
||||
for (let i = 0; i < timesToAdd; i++) {
|
||||
weightedFlags.push(flag);
|
||||
}
|
||||
}
|
||||
|
||||
if (weightedFlags.length > 0) {
|
||||
correctFlag =
|
||||
weightedFlags[Math.floor(Math.random() * weightedFlags.length)];
|
||||
} else {
|
||||
correctFlag = flags[Math.floor(Math.random() * flags.length)];
|
||||
}
|
||||
} else {
|
||||
// Normal random selection
|
||||
correctFlag = flags[Math.floor(Math.random() * flags.length)];
|
||||
}
|
||||
|
||||
const correctCountry = getCountryName(correctFlag).toLowerCase();
|
||||
const correctCountry = getCountryName(correctFlag).toLowerCase();
|
||||
|
||||
// Generate 3 wrong answers ensuring no duplicate country names
|
||||
const wrongOptions = [];
|
||||
@@ -356,7 +278,7 @@
|
||||
|
||||
while (wrongOptions.length < 3 && wrongOptions.length < flags.length - 1) {
|
||||
const randomFlag = flags[Math.floor(Math.random() * flags.length)];
|
||||
const randomCountry = getCountryName(randomFlag).toLowerCase();
|
||||
const randomCountry = getCountryName(randomFlag).toLowerCase();
|
||||
|
||||
if (!usedCountries.has(randomCountry)) {
|
||||
wrongOptions.push(randomFlag);
|
||||
@@ -373,9 +295,7 @@
|
||||
}
|
||||
|
||||
// Combine correct and wrong answers
|
||||
const allOptions = [correctFlag, ...wrongOptions].sort(
|
||||
() => Math.random() - 0.5,
|
||||
);
|
||||
const allOptions = [correctFlag, ...wrongOptions].sort(() => Math.random() - 0.5);
|
||||
currentQuestion = {
|
||||
type: questionType,
|
||||
correct: correctFlag,
|
||||
@@ -683,13 +603,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
function getCountryName(flag) {
|
||||
return flag.meta?.country || flag.name || "Unknown";
|
||||
}
|
||||
|
||||
function getFlagImage(flag) {
|
||||
return `/images/flags/${flag.path}`;
|
||||
}
|
||||
// use shared getCountryName from ../quizLogic/flags.js
|
||||
|
||||
function handleAchievementsUnlocked() {
|
||||
achievementCount = updateAchievementCount(achievementsComponent);
|
||||
|
||||
73
src/quizLogic/flags.js
Normal file
73
src/quizLogic/flags.js
Normal file
@@ -0,0 +1,73 @@
|
||||
// Utilities for loading and choosing flags used by quiz pages
|
||||
export async function loadFlags({ requireCapital = false } = {}) {
|
||||
try {
|
||||
const response = await fetch('/data/flags.json');
|
||||
const data = await response.json();
|
||||
|
||||
let flags = data.filter((flag) => {
|
||||
if (flag.disable) return false;
|
||||
if (!flag.meta?.country) return false;
|
||||
if (requireCapital && !flag.meta?.capital) return false;
|
||||
if (!flag.tags || !flag.tags.includes('Country')) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
// Remove duplicates based on country name
|
||||
const uniqueFlags = [];
|
||||
const seenCountries = new Set();
|
||||
|
||||
for (const flag of flags) {
|
||||
const countryName = (flag.meta.country || flag.name || '').toLowerCase().trim();
|
||||
if (!seenCountries.has(countryName)) {
|
||||
seenCountries.add(countryName);
|
||||
uniqueFlags.push(flag);
|
||||
}
|
||||
}
|
||||
|
||||
return uniqueFlags;
|
||||
} catch (err) {
|
||||
console.error('flags.loadFlags error', err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export function getCountryName(flag) {
|
||||
return flag?.meta?.country || flag?.name || 'Unknown';
|
||||
}
|
||||
|
||||
export function getFlagImage(flag) {
|
||||
return `/images/flags/${flag.path}`;
|
||||
}
|
||||
|
||||
// Pick a flag from a weighted list based on wrong/correct answer maps and settings
|
||||
export function pickWeightedFlag(flags, { focusWrongAnswers = false, reduceCorrectAnswers = false } = {}, wrongAnswers = new Map(), correctAnswers = new Map()) {
|
||||
if (!Array.isArray(flags) || flags.length === 0) return null;
|
||||
|
||||
// If no adaptive settings enabled, return a random flag
|
||||
if (!focusWrongAnswers && !reduceCorrectAnswers) {
|
||||
return flags[Math.floor(Math.random() * flags.length)];
|
||||
}
|
||||
|
||||
const weighted = [];
|
||||
for (const flag of flags) {
|
||||
const wrongCount = wrongAnswers.get(flag.name) || 0;
|
||||
const correctCount = correctAnswers.get(flag.name) || 0;
|
||||
|
||||
let weight = 1;
|
||||
if (focusWrongAnswers && wrongCount > 0) {
|
||||
weight = Math.min(wrongCount + 1, 4);
|
||||
}
|
||||
if (reduceCorrectAnswers && correctCount > 0) {
|
||||
weight = weight / Math.min(correctCount + 1, 4);
|
||||
}
|
||||
|
||||
const finalWeight = Math.max(0.25, weight);
|
||||
const times = Math.ceil(finalWeight);
|
||||
for (let i = 0; i < times; i++) weighted.push(flag);
|
||||
}
|
||||
|
||||
if (weighted.length > 0) {
|
||||
return weighted[Math.floor(Math.random() * weighted.length)];
|
||||
}
|
||||
return flags[Math.floor(Math.random() * flags.length)];
|
||||
}
|
||||
@@ -40,3 +40,32 @@ export function createNewSessionState(sessionLength = 10) {
|
||||
sessionRestoredFromReload: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Restore session helper: loads saved session state (if any) and returns a normalized object
|
||||
export function restoreSessionState(key) {
|
||||
const loaded = loadSessionState(key, null);
|
||||
if (!loaded) return null;
|
||||
|
||||
const session = {
|
||||
sessionInProgress: loaded.sessionInProgress || false,
|
||||
currentSessionQuestions: loaded.currentSessionQuestions || 0,
|
||||
sessionStats: loaded.sessionStats || {
|
||||
correct: 0,
|
||||
wrong: 0,
|
||||
skipped: 0,
|
||||
total: 0,
|
||||
sessionLength: loaded?.sessionStats?.sessionLength || 10,
|
||||
},
|
||||
score: loaded.score || { correct: 0, total: 0, skipped: 0 },
|
||||
currentQuestion: loaded.currentQuestion || null,
|
||||
selectedAnswer: loaded.selectedAnswer || null,
|
||||
showResult: loaded.showResult || false,
|
||||
gameState: loaded.gameState || "question",
|
||||
quizSubpage: loaded.quizSubpage || "quiz",
|
||||
sessionStartTime: loaded.sessionStartTime || null,
|
||||
questionKey: loaded.questionKey || 0,
|
||||
sessionRestoredFromReload: true,
|
||||
};
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { writable } from 'svelte/store';
|
||||
|
||||
export function applyTheme(theme) {
|
||||
let effectiveTheme = theme;
|
||||
if (theme === "system") {
|
||||
@@ -14,6 +16,12 @@ export function setTheme(newTheme) {
|
||||
if (newTheme === "light" || newTheme === "dark" || newTheme === "system") {
|
||||
// Persist choice and apply immediately
|
||||
localStorage.setItem("theme", newTheme);
|
||||
// Update reactive store so $themeStore updates everywhere immediately
|
||||
try {
|
||||
themeStore.set(newTheme);
|
||||
} catch (e) {
|
||||
// If themeStore isn't initialized yet, ignore — it will be set on module init
|
||||
}
|
||||
console.log("[Theme] setTheme:", newTheme);
|
||||
setTimeout(() => applyTheme(newTheme), 0);
|
||||
return newTheme;
|
||||
@@ -25,5 +33,4 @@ export function getStoredTheme(defaultTheme = "system") {
|
||||
}
|
||||
|
||||
// Svelte store for reactive theme across components
|
||||
import { writable } from 'svelte/store';
|
||||
export const themeStore = writable(getStoredTheme());
|
||||
|
||||
Reference in New Issue
Block a user