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 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}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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