Added streak percentage

This commit is contained in:
George Powell
2026-02-21 16:17:06 -05:00
parent c3307b3920
commit 6554ef8f41
6 changed files with 143 additions and 17 deletions

View File

@@ -18,7 +18,7 @@
shareResult,
copyToClipboard as clipboardCopy,
} from "$lib/utils/share";
import { fetchStreak } from "$lib/utils/streak";
import { fetchStreak, fetchStreakPercentile } from "$lib/utils/streak";
import {
submitCompletion,
fetchExistingStats,
@@ -41,6 +41,7 @@
let showWinScreen = $state(false);
let statsData = $state<StatsData | null>(null);
let streak = $state(0);
let streakPercentile = $state<number | null>(null);
const persistence = createGamePersistence(
() => dailyVerse.date,
@@ -217,6 +218,11 @@
const localDate = new Date().toLocaleDateString("en-CA");
fetchStreak(persistence.anonymousId, localDate).then((result) => {
streak = result;
if (result >= 2) {
fetchStreakPercentile(result, localDate).then((p) => {
streakPercentile = p;
});
}
});
});
@@ -308,6 +314,7 @@
onChapterGuessCompleted={persistence.onChapterGuessCompleted}
shareText={getShareText()}
{streak}
{streakPercentile}
/>
</div>
{/if}

View File

@@ -0,0 +1,72 @@
import { json, error } from '@sveltejs/kit';
import type { RequestHandler } from './$types';
import { db } from '$lib/server/db';
import { dailyCompletions } from '$lib/server/db/schema';
import { desc } from 'drizzle-orm';
export const GET: RequestHandler = async ({ url }) => {
const streakParam = url.searchParams.get('streak');
const localDate = url.searchParams.get('localDate');
if (!streakParam || !localDate) {
error(400, 'Missing streak or localDate');
}
const targetStreak = parseInt(streakParam, 10);
if (isNaN(targetStreak) || targetStreak < 1) {
error(400, 'Invalid streak');
}
// Fetch all completions ordered by anonymous_id and date desc
// so we can walk each user's history to compute their current streak.
const rows = await db
.select({
anonymousId: dailyCompletions.anonymousId,
date: dailyCompletions.date,
})
.from(dailyCompletions)
.orderBy(desc(dailyCompletions.date));
// Group dates by user
const byUser = new Map<string, string[]>();
for (const row of rows) {
const list = byUser.get(row.anonymousId);
if (list) {
list.push(row.date);
} else {
byUser.set(row.anonymousId, [row.date]);
}
}
// Calculate the current streak for each user
const streaks: number[] = [];
for (const [, dates] of byUser) {
// dates are already desc-sorted
const dateSet = new Set(dates);
let streak = 0;
let cursor = new Date(`${localDate}T00:00:00`);
while (true) {
const dateStr = cursor.toLocaleDateString('en-CA');
if (!dateSet.has(dateStr)) break;
streak++;
cursor.setDate(cursor.getDate() - 1);
}
streaks.push(streak);
}
// Only count users who have an active streak (streak >= 1)
const activeStreaks = streaks.filter((s) => s >= 1);
if (activeStreaks.length === 0) {
return json({ percentile: 100 });
}
// Percentage of active-streak users who have a streak >= targetStreak
const atOrAbove = activeStreaks.filter((s) => s >= targetStreak).length;
const raw = (atOrAbove / activeStreaks.length) * 100;
const percentile = raw < 1 ? Math.round(raw * 100) / 100 : Math.round(raw);
return json({ percentile });
};