sunrise-sunset: current streak length

This commit is contained in:
Thomas 2023-01-29 23:15:03 +00:00
parent ac98297cff
commit 29e4cb0b6d
4 changed files with 142 additions and 0 deletions

View file

@ -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 @@
<ScoreCardSection
doesUserHaveGuessingHistory={$guessingHistory.mostRecentGuessDate !==
undefined}
{currentStreakLength}
correctGuessCount={$guessingHistory.correctDays.length}
incorrectGuessCount={$guessingHistory.incorrectDays.length}
/>

View file

@ -2,6 +2,7 @@
export let doesUserHaveGuessingHistory: boolean;
export let correctGuessCount: number;
export let incorrectGuessCount: number;
export let currentStreakLength: number;
$: totalGuessCount = correctGuessCount + incorrectGuessCount;
</script>
@ -14,6 +15,10 @@
You've made {totalGuessCount}
{totalGuessCount === 1 ? "guess" : "guesses"} so far.
</p>
<p class="score__text">
Your current streak is {currentStreakLength}
{currentStreakLength === 1 ? "day" : "days"}.
</p>
<p class="score__text">
You've guessed correctly {Number(correctGuessCount / totalGuessCount) *
100}% of the time.

View file

@ -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);
});
});

View file

@ -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;
}
}
}
}