sunrise-sunset: current streak length
This commit is contained in:
parent
ac98297cff
commit
29e4cb0b6d
4 changed files with 142 additions and 0 deletions
|
|
@ -9,6 +9,7 @@
|
||||||
import OptionsSection from "./OptionsSection.svelte";
|
import OptionsSection from "./OptionsSection.svelte";
|
||||||
import ScoreCardSection from "./ScoreCardSection.svelte";
|
import ScoreCardSection from "./ScoreCardSection.svelte";
|
||||||
import NotificationSection from "./NotificationSection.svelte";
|
import NotificationSection from "./NotificationSection.svelte";
|
||||||
|
import { SunriseSunsetStreakCalculator } from "./SunriseSunsetStreakCalculator.js";
|
||||||
import type { ISunriseSunsetGuessingHistory } from "./ISunriseSunsetGuessingHistory.js";
|
import type { ISunriseSunsetGuessingHistory } from "./ISunriseSunsetGuessingHistory.js";
|
||||||
|
|
||||||
let hasGuessingHistoryBeenLoaded = false;
|
let hasGuessingHistoryBeenLoaded = false;
|
||||||
|
|
@ -28,6 +29,9 @@
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const todaysDateString = formatDate(now, "yyyy-MM-dd");
|
const todaysDateString = formatDate(now, "yyyy-MM-dd");
|
||||||
const localStorageKey = "sunrise-sunset-guessing-history";
|
const localStorageKey = "sunrise-sunset-guessing-history";
|
||||||
|
let currentStreakLength = 0;
|
||||||
|
|
||||||
|
const streakCalculator = new SunriseSunsetStreakCalculator(todaysDateString);
|
||||||
|
|
||||||
$: picture = data.body.photo;
|
$: picture = data.body.photo;
|
||||||
|
|
||||||
|
|
@ -74,9 +78,13 @@
|
||||||
|
|
||||||
guessingHistory.subscribe((value) => {
|
guessingHistory.subscribe((value) => {
|
||||||
if (!hasGuessingHistoryBeenLoaded) {
|
if (!hasGuessingHistoryBeenLoaded) {
|
||||||
|
console.log(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentStreakLength = streakCalculator.getStreakLength(
|
||||||
|
$guessingHistory.correctDays
|
||||||
|
);
|
||||||
localStorage.setItem(localStorageKey, JSON.stringify(value));
|
localStorage.setItem(localStorageKey, JSON.stringify(value));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -133,6 +141,7 @@
|
||||||
<ScoreCardSection
|
<ScoreCardSection
|
||||||
doesUserHaveGuessingHistory={$guessingHistory.mostRecentGuessDate !==
|
doesUserHaveGuessingHistory={$guessingHistory.mostRecentGuessDate !==
|
||||||
undefined}
|
undefined}
|
||||||
|
{currentStreakLength}
|
||||||
correctGuessCount={$guessingHistory.correctDays.length}
|
correctGuessCount={$guessingHistory.correctDays.length}
|
||||||
incorrectGuessCount={$guessingHistory.incorrectDays.length}
|
incorrectGuessCount={$guessingHistory.incorrectDays.length}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
export let doesUserHaveGuessingHistory: boolean;
|
export let doesUserHaveGuessingHistory: boolean;
|
||||||
export let correctGuessCount: number;
|
export let correctGuessCount: number;
|
||||||
export let incorrectGuessCount: number;
|
export let incorrectGuessCount: number;
|
||||||
|
export let currentStreakLength: number;
|
||||||
|
|
||||||
$: totalGuessCount = correctGuessCount + incorrectGuessCount;
|
$: totalGuessCount = correctGuessCount + incorrectGuessCount;
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -14,6 +15,10 @@
|
||||||
You've made {totalGuessCount}
|
You've made {totalGuessCount}
|
||||||
{totalGuessCount === 1 ? "guess" : "guesses"} so far.
|
{totalGuessCount === 1 ? "guess" : "guesses"} so far.
|
||||||
</p>
|
</p>
|
||||||
|
<p class="score__text">
|
||||||
|
Your current streak is {currentStreakLength}
|
||||||
|
{currentStreakLength === 1 ? "day" : "days"}.
|
||||||
|
</p>
|
||||||
<p class="score__text">
|
<p class="score__text">
|
||||||
You've guessed correctly {Number(correctGuessCount / totalGuessCount) *
|
You've guessed correctly {Number(correctGuessCount / totalGuessCount) *
|
||||||
100}% of the time.
|
100}% of the time.
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
52
src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.ts
Normal file
52
src/routes/sunrise-sunset/SunriseSunsetStreakCalculator.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue