-
Notifications
You must be signed in to change notification settings - Fork 58
Expand file tree
/
Copy pathforegroundTimeTracker.ts
More file actions
114 lines (98 loc) · 3.35 KB
/
Copy pathforegroundTimeTracker.ts
File metadata and controls
114 lines (98 loc) · 3.35 KB
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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import { isNumber } from './utils';
import { createVault, BaseVault } from './vault';
import { VaultKind } from './constants';
export default class ForegroundTimeTracker {
private isTrackerActive: boolean = false;
private localStorageName: string = '';
private timerVault: BaseVault<number>;
public startTime: number = 0;
public totalTime: number = 0;
constructor(timerKey: string) {
this.localStorageName = `mprtcl-tos-${timerKey}`;
this.timerVault = createVault<number>(this.localStorageName, VaultKind.LocalStorage);
this.loadTimeFromStorage();
this.addHandlers();
if (document.hidden === false) {
this.startTracking();
}
}
private addHandlers(): void {
// when user switches tabs or minimizes the window
document.addEventListener('visibilitychange', () =>
this.handleVisibilityChange()
);
// when user switches to another application
window.addEventListener('blur', () => this.handleWindowBlur());
// when window gains focus
window.addEventListener('focus', () => this.handleWindowFocus());
// this ensures that timers between tabs are in sync
window.addEventListener('storage', event => this.syncAcrossTabs(event));
// when user closes tab, refreshes, or navigates to another page via link
window.addEventListener('beforeunload', () =>
this.updateTimeInPersistence()
);
}
private handleVisibilityChange(): void {
if (document.hidden) {
this.stopTracking();
} else {
this.startTracking();
}
}
private handleWindowBlur(): void {
if (this.isTrackerActive) {
this.stopTracking();
}
}
private handleWindowFocus(): void {
if (!this.isTrackerActive) {
this.startTracking();
}
}
private syncAcrossTabs(event: StorageEvent): void {
if (event.key === this.localStorageName && event.newValue !== null) {
const newTime = parseFloat(event.newValue) || 0;
this.totalTime = newTime;
}
}
public updateTimeInPersistence(): void {
if (this.isTrackerActive) {
this.timerVault.store(Math.round(this.totalTime));
}
}
private loadTimeFromStorage(): void {
const storedTime = this.timerVault.retrieve();
if (isNumber(storedTime) && storedTime !== null) {
this.totalTime = storedTime;
}
}
private startTracking(): void {
if (!document.hidden) {
this.startTime = Math.floor(performance.now());
this.isTrackerActive = true;
}
}
private stopTracking(): void {
if (this.isTrackerActive) {
this.setTotalTime();
this.updateTimeInPersistence();
this.isTrackerActive = false;
}
}
private setTotalTime(): void {
if (this.isTrackerActive) {
const now = Math.floor(performance.now());
this.totalTime += now - this.startTime;
this.startTime = now;
}
}
public getTimeInForeground(): number {
this.setTotalTime();
this.updateTimeInPersistence();
return this.totalTime;
}
public resetTimer(): void {
this.totalTime = 0;
this.updateTimeInPersistence();
}
}