Play hub: sync game status at the hub, not only on game-open
Reported sync gaps: the hub showed "Play" until you opened each game, and a game synced only if you'd reopened it on that device (so a desktop win that was never reopened never reached the server). Root cause: the /play hub read card status from localStorage only and never talked to the server — sync happened exclusively inside the game components on mount. Now the hub itself reconciles every game (word 5/6 + wordsearch small/med/large) with the server on load (signed-in): pushes this device's local state and writes the merged result back to localStorage, then refreshes the cards. So statuses appear cross-device WITHOUT opening each game, and local progress uploads even for games not reopened. Word Search card status derived from the (completion- gated) ms. 237 pytest + 11 vitest green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
import { goto, afterNavigate } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { getJSON } from '$lib/api.js';
|
||||
import { pushGameState } from '$lib/gamesync.js';
|
||||
import { auth } from '$lib/auth.svelte.js';
|
||||
import WordGame from '$lib/components/WordGame.svelte';
|
||||
import WordSearchGame from '$lib/components/WordSearchGame.svelte';
|
||||
|
||||
@@ -50,6 +52,29 @@
|
||||
return Math.floor(s / 60) + ':' + String(s % 60).padStart(2, '0');
|
||||
}
|
||||
|
||||
// The hub itself reconciles every game with the server (signed-in), so cards
|
||||
// show cross-device status WITHOUT having to open each game first, and this
|
||||
// device's local progress gets uploaded even for games it hasn't reopened.
|
||||
async function syncOne(g, v, key) {
|
||||
let local = null;
|
||||
try { local = JSON.parse(localStorage.getItem(key) || 'null'); } catch { /* ignore */ }
|
||||
const merged = await pushGameState(g, v, date, local || {});
|
||||
if (!merged) return;
|
||||
if (g === 'wordsearch') merged.status = merged.ms ? 'done' : 'playing'; // card reads .status
|
||||
try { localStorage.setItem(key, JSON.stringify(merged)); } catch { /* ignore */ }
|
||||
}
|
||||
async function syncAllGames() {
|
||||
if (!auth.user || !date) return;
|
||||
await Promise.allSettled([
|
||||
syncOne('word', '5', `goodnews:word:5:${date}`),
|
||||
syncOne('word', '6', `goodnews:word:6:${date}`),
|
||||
syncOne('wordsearch', 'small', `goodnews:wordsearch:small:${date}`),
|
||||
syncOne('wordsearch', 'med', `goodnews:wordsearch:med:${date}`),
|
||||
syncOne('wordsearch', 'large', `goodnews:wordsearch:large:${date}`),
|
||||
]);
|
||||
refreshStatus();
|
||||
}
|
||||
|
||||
// Hub card one-liners
|
||||
function wordLabel() {
|
||||
const a = wordStatus['5'], b = wordStatus['6'];
|
||||
@@ -123,6 +148,7 @@
|
||||
try { date = (await getJSON('/api/puzzle/word?variant=5')).date; } catch { /* offline */ }
|
||||
try { wsTheme = (await getJSON('/api/puzzle/wordsearch?variant=med')).theme; } catch { /* offline */ }
|
||||
refreshStatus();
|
||||
syncAllGames(); // signed-in: pull cross-device status into the cards + upload local progress
|
||||
});
|
||||
// Refresh hub/selection statuses whenever we land on a screen (incl. Back).
|
||||
afterNavigate(() => refreshStatus());
|
||||
|
||||
Reference in New Issue
Block a user