diff --git a/src/routes/sunrise-sunset/+page.svelte b/src/routes/sunrise-sunset/+page.svelte index 66c4ccf..b8202c2 100644 --- a/src/routes/sunrise-sunset/+page.svelte +++ b/src/routes/sunrise-sunset/+page.svelte @@ -9,6 +9,7 @@ import OptionsSection from "./OptionsSection.svelte"; import ScoreCardSection from "./ScoreCardSection.svelte"; import NotificationSection from "./NotificationSection.svelte"; + import { SunriseSunsetStreakCalculator } from "./SunriseSunsetStreakCalculator.js"; import type { ISunriseSunsetGuessingHistory } from "./ISunriseSunsetGuessingHistory.js"; let hasGuessingHistoryBeenLoaded = false; @@ -28,6 +29,9 @@ const now = new Date(); const todaysDateString = formatDate(now, "yyyy-MM-dd"); const localStorageKey = "sunrise-sunset-guessing-history"; + let currentStreakLength = 0; + + const streakCalculator = new SunriseSunsetStreakCalculator(todaysDateString); $: picture = data.body.photo; @@ -74,9 +78,13 @@ guessingHistory.subscribe((value) => { if (!hasGuessingHistoryBeenLoaded) { + console.log(value); return; } + currentStreakLength = streakCalculator.getStreakLength( + $guessingHistory.correctDays + ); localStorage.setItem(localStorageKey, JSON.stringify(value)); }); @@ -133,6 +141,7 @@ diff --git a/src/routes/sunrise-sunset/ScoreCardSection.svelte b/src/routes/sunrise-sunset/ScoreCardSection.svelte index e59336d..df5f22b 100644 --- a/src/routes/sunrise-sunset/ScoreCardSection.svelte +++ b/src/routes/sunrise-sunset/ScoreCardSection.svelte @@ -2,6 +2,7 @@ export let doesUserHaveGuessingHistory: boolean; export let correctGuessCount: number; export let incorrectGuessCount: number; + export let currentStreakLength: number; $: totalGuessCount = correctGuessCount + incorrectGuessCount; @@ -14,6 +15,10 @@ You've made {totalGuessCount} {totalGuessCount === 1 ? "guess" : "guesses"} so far.

+

+ Your current streak is {currentStreakLength} + {currentStreakLength === 1 ? "day" : "days"}. +

You've guessed correctly {Number(correctGuessCount / totalGuessCount) * 100}% of the time. diff --git a/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.test.ts b/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.test.ts new file mode 100644 index 0000000..925b80d --- /dev/null +++ b/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.test.ts @@ -0,0 +1,76 @@ +import { describe, it, expect } from 'vitest'; +import { SunriseSunsetStreakCalculator } from './SunriseSunsetStreakCalculator.js'; + +describe('SunriseSunsetStreakCalculator', () => { + const anyDay = '2023-01-29'; + it(`should recognise an empty streak`, () => { + // GIVEN + const correctDays = []; + + // WHEN + const currentStreakLength = new SunriseSunsetStreakCalculator(anyDay).getStreakLength(correctDays); + + // THEN + expect(currentStreakLength).toBe(0); + }); + + it(`should return a one-day streak when there is a gap between today and a previous streak`, () => { + // GIVEN + const correctDays = ['2023-01-29', '2023-01-27', '2023-01-26']; + const today = '2023-01-29'; + + // WHEN + const currentStreakLength = new SunriseSunsetStreakCalculator(today).getStreakLength(correctDays); + + // THEN + expect(currentStreakLength).toBe(1); + }); + + it(`should return a two day stream if it's the most recent`, () => { + // GIVEN + const correctDays = ['2023-01-29', '2023-01-28', '2023-01-26', '2023-01-25', '2023-01-24']; + const today = '2023-01-29'; + + // WHEN + const currentStreakLength = new SunriseSunsetStreakCalculator(today).getStreakLength(correctDays); + + // THEN + expect(currentStreakLength).toBe(2); + }); + + it(`should return recognise a one day streak`, () => { + // GIVEN + const correctDays = ['2023-01-28']; + const today = '2023-01-28'; + + // WHEN + const currentStreakLength = new SunriseSunsetStreakCalculator(today).getStreakLength(correctDays); + + // THEN + expect(currentStreakLength).toBe(1); + }); + + it(`should recognise a two day streak`, () => { + // GIVEN + const correctDays = ['2023-01-28', '2023-01-27']; + const today = '2023-01-28'; + + // WHEN + const currentStreakLength = new SunriseSunsetStreakCalculator(today).getStreakLength(correctDays); + + // THEN + expect(currentStreakLength).toBe(2); + }); + + it(`should recognise a three day streak`, () => { + // GIVEN + const correctDays = ['2023-01-28', '2023-01-27', '2023-01-26']; + const today = '2023-01-28'; + + // WHEN + const currentStreakLength = new SunriseSunsetStreakCalculator(today).getStreakLength(correctDays); + + // THEN + expect(currentStreakLength).toBe(3); + }); +}); diff --git a/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.ts b/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.ts new file mode 100644 index 0000000..e5d3c2c --- /dev/null +++ b/src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.ts @@ -0,0 +1,52 @@ +import { parse, isAfter, differenceInCalendarDays } from 'date-fns'; + +export class SunriseSunsetStreakCalculator { + private readonly todayDate: Date; + constructor(private readonly today: string) { + this.todayDate = parse(today, 'yyyy-MM-dd', new Date()); + } + + getStreakLength(correctDays: string[]): number { + if (correctDays.length === 0) { + console.log(`No correct days, returning 0.`); + return 0; + } else if (!correctDays.some((day) => day === this.today)) { + console.log(`Today is not in the list of correct days`); + return 0; + } + + const daysAsDates = correctDays.map((day) => parse(day, 'yyyy-MM-dd', new Date())); + const sortedDays = daysAsDates.sort((a, b) => b.getTime() - a.getTime()); + + const sortedDaysWithoutToday = correctDays + .filter((day) => day !== this.today) + .map((day) => parse(day, 'yyyy-MM-dd', new Date())) + .sort((a, b) => b.getTime() - a.getTime()); + const daysBetweenTodayAndMostRecentDay = differenceInCalendarDays(this.todayDate, sortedDaysWithoutToday[0]); + + if (daysBetweenTodayAndMostRecentDay > 1) { + console.log(`Today is more than one day after the most recent correct day`); + return 1; + } + + let streakLength = 1; + for (const [index, day] of sortedDays.entries()) { + const nextDay = sortedDays[index + 1]; + + if (nextDay === undefined) { + console.log(`No next day, returning streak length of ${streakLength}`); + return streakLength; + } + + const daysBetween = differenceInCalendarDays(day, nextDay); + console.log(`Days Between ${day} - ${nextDay}`, daysBetween); + + if (daysBetween === 1) { + streakLength++; + } else { + console.log(`Days between is not 1, returning streak length of ${streakLength}`); + return streakLength; + } + } + } +}