1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
const STORAGE_KEY = 'typerpunk:profile';
// Caps how many past results the progress chart can show - unbounded
// growth would slowly bloat localStorage for a long-lived install with no
// benefit, since the chart only ever renders the most recent entries anyway.
const MAX_HISTORY = 100;
function defaults() {
return {
testsCompleted: 0,
totalTimeSeconds: 0,
totalWpm: 0,
totalAccuracy: 0,
bestWpm: 0,
lastTestDate: null,
streakDays: 0,
history: [],
};
}
function load() {
try {
const raw = JSON.parse(localStorage.getItem(STORAGE_KEY));
return raw && typeof raw === 'object' ? { ...defaults(), ...raw } : defaults();
} catch {
return defaults();
}
}
export function getProfileStats() {
const s = load();
return {
...s,
averageWpm: s.testsCompleted ? Math.round(s.totalWpm / s.testsCompleted) : 0,
averageAccuracy: s.testsCompleted ? Math.round(s.totalAccuracy / s.testsCompleted) : 0,
};
}
// Called once per finished test (any mode, including custom text) - these
// are lifetime activity totals, not per-mode bests, so unlike pb.js every
// completed test counts, not just new records.
export function recordTest({ wpm, accuracy, time }) {
const s = load();
s.testsCompleted += 1;
s.totalTimeSeconds += time;
s.totalWpm += wpm;
s.totalAccuracy += accuracy;
s.bestWpm = Math.max(s.bestWpm, wpm);
const today = new Date().toDateString();
const lastDay = s.lastTestDate ? new Date(s.lastTestDate).toDateString() : null;
if (lastDay !== today) {
const yesterday = new Date(Date.now() - 86400000).toDateString();
s.streakDays = lastDay === yesterday ? s.streakDays + 1 : 1;
}
s.lastTestDate = Date.now();
s.history.push({ wpm, accuracy, date: s.lastTestDate });
if (s.history.length > MAX_HISTORY) s.history = s.history.slice(-MAX_HISTORY);
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(s)); } catch {}
return s;
}
|