mirror of
https://github.com/pupperpowell/bibdle.git
synced 2026-04-06 01:43:32 -04:00
WIP new share menu
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import { browser } from "$app/environment";
|
||||
import { evaluateGuess, generateUUID, type Guess } from "$lib/utils/game";
|
||||
|
||||
// Returns a stable anonymous ID for this browser, creating one if it doesn't exist yet.
|
||||
// Used to attribute stats to a player who hasn't signed in.
|
||||
function getOrCreateAnonymousId(): string {
|
||||
if (!browser) return "";
|
||||
const key = "bibdle-anonymous-id";
|
||||
@@ -12,6 +14,9 @@ function getOrCreateAnonymousId(): string {
|
||||
return id;
|
||||
}
|
||||
|
||||
// Reactive store that keeps in-memory game state in sync with localStorage.
|
||||
// Accepts getter functions (rather than plain values) so Svelte's reactivity
|
||||
// system can track dependencies and re-run effects when they change.
|
||||
export function createGamePersistence(
|
||||
getDate: () => string,
|
||||
getReference: () => string,
|
||||
@@ -24,7 +29,8 @@ export function createGamePersistence(
|
||||
let chapterGuessCompleted = $state(false);
|
||||
let chapterCorrect = $state(false);
|
||||
|
||||
// Initialize anonymous ID and load persisted flags
|
||||
// On mount (and if the user logs in/out), resolve the player's identity and
|
||||
// restore per-day flags from localStorage.
|
||||
$effect(() => {
|
||||
if (!browser) return;
|
||||
|
||||
@@ -36,6 +42,7 @@ export function createGamePersistence(
|
||||
anonymousId = getOrCreateAnonymousId();
|
||||
}
|
||||
|
||||
// Tell Umami analytics which player this is so events are grouped correctly.
|
||||
if ((window as any).umami) {
|
||||
(window as any).umami.identify(anonymousId);
|
||||
}
|
||||
@@ -43,8 +50,11 @@ export function createGamePersistence(
|
||||
const date = getDate();
|
||||
const reference = getReference();
|
||||
|
||||
// Restore whether today's completion was already submitted to the server.
|
||||
statsSubmitted = localStorage.getItem(`bibdle-stats-submitted-${date}`) === "true";
|
||||
|
||||
// Restore the chapter bonus guess result. The stored value includes the
|
||||
// chapter the player selected, so we can re-derive whether it was correct.
|
||||
const chapterGuessKey = `bibdle-chapter-guess-${reference}`;
|
||||
chapterGuessCompleted = localStorage.getItem(chapterGuessKey) !== null;
|
||||
if (chapterGuessCompleted) {
|
||||
@@ -58,7 +68,9 @@ export function createGamePersistence(
|
||||
}
|
||||
});
|
||||
|
||||
// Load saved guesses from localStorage
|
||||
// On mount (and if the date or correct answer changes), load today's guesses
|
||||
// from localStorage and reconstruct them as typed Guess objects by re-evaluating
|
||||
// each stored book ID against the correct answer.
|
||||
$effect(() => {
|
||||
if (!browser) return;
|
||||
|
||||
@@ -72,13 +84,14 @@ export function createGamePersistence(
|
||||
}
|
||||
|
||||
let savedIds: string[] = JSON.parse(saved);
|
||||
savedIds = Array.from(new Set(savedIds));
|
||||
savedIds = Array.from(new Set(savedIds)); // deduplicate, just in case
|
||||
guesses = savedIds
|
||||
.map((bookId) => evaluateGuess(bookId, correctBookId))
|
||||
.filter((g): g is Guess => g !== null);
|
||||
});
|
||||
|
||||
// Save guesses to localStorage whenever they change
|
||||
// Persist guesses to localStorage whenever they change. Only the book IDs are
|
||||
// stored — the full Guess shape is re-derived on load (see effect above).
|
||||
$effect(() => {
|
||||
if (!browser) return;
|
||||
const date = getDate();
|
||||
@@ -88,12 +101,16 @@ export function createGamePersistence(
|
||||
);
|
||||
});
|
||||
|
||||
// Called after stats are successfully submitted to the server so that
|
||||
// returning to the page doesn't trigger a duplicate submission.
|
||||
function markStatsSubmitted() {
|
||||
if (!browser) return;
|
||||
statsSubmitted = true;
|
||||
localStorage.setItem(`bibdle-stats-submitted-${getDate()}`, "true");
|
||||
}
|
||||
|
||||
// Marks the win as tracked for analytics. Returns true the first time (new
|
||||
// win), false on subsequent calls so the analytics event fires exactly once.
|
||||
function markWinTracked() {
|
||||
if (!browser) return;
|
||||
const key = `bibdle-win-tracked-${getDate()}`;
|
||||
@@ -102,11 +119,16 @@ export function createGamePersistence(
|
||||
return true;
|
||||
}
|
||||
|
||||
// Returns true if the win has already been tracked in a previous render/session.
|
||||
// Used to skip the animation delay when returning to an already-won game.
|
||||
function isWinAlreadyTracked(): boolean {
|
||||
if (!browser) return false;
|
||||
return localStorage.getItem(`bibdle-win-tracked-${getDate()}`) === "true";
|
||||
}
|
||||
|
||||
// Overwrites local state with the server's authoritative guess record.
|
||||
// Called when a logged-in user opens the game on a new device so their
|
||||
// progress from another device is restored.
|
||||
function hydrateFromServer(guessIds: string[]) {
|
||||
if (!browser) return;
|
||||
const correctBookId = getCorrectBookId();
|
||||
@@ -116,6 +138,8 @@ export function createGamePersistence(
|
||||
.filter((g): g is Guess => g !== null);
|
||||
}
|
||||
|
||||
// Called by the WinScreen after the player submits their chapter bonus guess.
|
||||
// Reads the result written to localStorage by WinScreen and updates reactive state.
|
||||
function onChapterGuessCompleted() {
|
||||
if (!browser) return;
|
||||
chapterGuessCompleted = true;
|
||||
|
||||
Reference in New Issue
Block a user