diff --git a/src/lib/components/StreakCounter.svelte b/src/lib/components/StreakCounter.svelte new file mode 100644 index 0000000..e0254bf --- /dev/null +++ b/src/lib/components/StreakCounter.svelte @@ -0,0 +1,98 @@ + + +
+
+ {streak?.length ?? 0} + Tage +
+ +
+ + diff --git a/src/lib/stores/rosaryStreak.svelte.ts b/src/lib/stores/rosaryStreak.svelte.ts new file mode 100644 index 0000000..8007911 --- /dev/null +++ b/src/lib/stores/rosaryStreak.svelte.ts @@ -0,0 +1,101 @@ +import { browser } from '$app/environment'; + +const STORAGE_KEY = 'rosary_streak'; + +interface StreakData { + length: number; + lastPrayed: string | null; // ISO date string (YYYY-MM-DD) +} + +function getToday(): string { + return new Date().toISOString().split('T')[0]; +} + +function isYesterday(dateStr: string): boolean { + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() - 1); + return dateStr === yesterday.toISOString().split('T')[0]; +} + +function loadFromStorage(): StreakData { + if (!browser) return { length: 0, lastPrayed: null }; + + try { + const stored = localStorage.getItem(STORAGE_KEY); + if (stored) { + return JSON.parse(stored); + } + } catch { + // Invalid data, return default + } + return { length: 0, lastPrayed: null }; +} + +function saveToStorage(data: StreakData): void { + if (!browser) return; + localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); +} + +class RosaryStreakStore { + #length = $state(0); + #lastPrayed = $state(null); + #initialized = false; + + constructor() { + if (browser) { + this.#init(); + } + } + + #init() { + if (this.#initialized) return; + const data = loadFromStorage(); + this.#length = data.length; + this.#lastPrayed = data.lastPrayed; + this.#initialized = true; + } + + get length() { + return this.#length; + } + + get lastPrayed() { + return this.#lastPrayed; + } + + get prayedToday(): boolean { + return this.#lastPrayed === getToday(); + } + + recordPrayer(): void { + const today = getToday(); + + // Already prayed today - no change + if (this.#lastPrayed === today) { + return; + } + + // Determine new streak length + let newLength: number; + if (this.#lastPrayed && isYesterday(this.#lastPrayed)) { + // Continuing streak from yesterday + newLength = this.#length + 1; + } else { + // Starting new streak (either first time or gap > 1 day) + newLength = 1; + } + + this.#length = newLength; + this.#lastPrayed = today; + saveToStorage({ length: newLength, lastPrayed: today }); + } +} + +let instance: RosaryStreakStore | null = null; + +export function getRosaryStreak(): RosaryStreakStore { + if (!instance) { + instance = new RosaryStreakStore(); + } + return instance; +} diff --git a/src/routes/glaube/rosenkranz/+page.svelte b/src/routes/glaube/rosenkranz/+page.svelte index a05af63..f2fa33d 100644 --- a/src/routes/glaube/rosenkranz/+page.svelte +++ b/src/routes/glaube/rosenkranz/+page.svelte @@ -16,6 +16,7 @@ import CounterButton from "$lib/components/CounterButton.svelte"; import BibleModal from "$lib/components/BibleModal.svelte"; import Toggle from "$lib/components/Toggle.svelte"; import LanguageToggle from "$lib/components/LanguageToggle.svelte"; +import StreakCounter from "$lib/components/StreakCounter.svelte"; let { data } = $props(); @@ -888,14 +889,29 @@ h1 { margin-bottom: 2rem; } +/* Controls row: toggles + streak counter */ +.controls-row { + display: flex; + flex-direction: column; + align-items: center; + gap: 1.5rem; + margin: 0 auto 2rem auto; +} + +@media (min-width: 700px) { + .controls-row { + flex-direction: row; + justify-content: center; + gap: 3rem; + } +} + /* Toggle controls container */ .toggle-controls { display: flex; flex-direction: column; align-items: flex-start; gap: 0.5rem; - max-width: fit-content; - margin: 0 auto 2rem auto; } /* Mystery selector grid */ @@ -1208,17 +1224,21 @@ l536 389l-209 -629zM1671 934l-370 267l150 436l-378 -271l-371 271q8 -34 15 -68q10 {/if} - -
- - + +
+
+ + - - + + +
+ +