Skip to content

Commit c266179

Browse files
authored
Merge pull request #39 from twentyTwo/develop
Added Focus Timeout
2 parents e4970a6 + 5e71a33 commit c266179

5 files changed

Lines changed: 89 additions & 25 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Simple Coding Time Tracker is a powerful extension for Visual Studio Code that h
2525
- **Configurable Settings**:
2626
- Save Interval: Customize how often your coding time data is saved (default: 5 seconds)
2727
- Inactivity Timeout: Set how long to wait before stopping the timer when no activity is detected (default: 5 minutes)
28+
- Focus Timeout: Set how long to continue tracking after VS Code loses focus (default: 60 seconds)
2829

2930
## Installation
3031

package.json

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,19 @@
1919
"contributes": {
2020
"configuration": {
2121
"title": "Simple Coding Time Tracker",
22-
"properties": {
23-
"simpleCodingTimeTracker.saveInterval": {
22+
"properties": { "simpleCodingTimeTracker.saveInterval": {
2423
"type": "number",
2524
"default": 5,
26-
"description": "Interval in seconds to save the current coding session"
25+
"description": "How often to save coding time data (in seconds)"
26+
}, "simpleCodingTimeTracker.inactivityTimeout": {
27+
"type": "number",
28+
"default": 150,
29+
"description": "Stop tracking after no keyboard/mouse activity (in seconds)"
2730
},
28-
"simpleCodingTimeTracker.inactivityTimeout": {
31+
"simpleCodingTimeTracker.focusTimeout": {
2932
"type": "number",
30-
"default": 300,
31-
"description": "Time in seconds of inactivity before tracking stops (default: 300 seconds = 5 minutes)"
33+
"default": 180,
34+
"description": "Continue tracking when switching apps (in seconds)"
3235
}
3336
}
3437
},

src/database.ts

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,16 @@ export interface SummaryData {
1414

1515
export class Database {
1616
private context: vscode.ExtensionContext;
17+
private entries: TimeEntry[] | null = null;
1718

1819
constructor(context: vscode.ExtensionContext) {
1920
this.context = context;
20-
// Log the storage URI path
21-
const storagePath = this.context.storageUri?.fsPath;
22-
if (storagePath) {
23-
console.log('Extension Data Storage Path:', storagePath);
24-
//vscode.window.showInformationMessage(`Data is stored at: ${storagePath}`);
21+
// Initialize storage if empty
22+
if (!this.context.globalState.get('timeEntries')) {
23+
this.context.globalState.update('timeEntries', []);
2524
}
25+
// Load entries into memory
26+
this.entries = this.context.globalState.get<TimeEntry[]>('timeEntries', []);
2627
}
2728

2829
private getLocalDateString(date: Date): string {
@@ -34,21 +35,33 @@ export class Database {
3435
async addEntry(date: Date, project: string, timeSpent: number) {
3536
const dateString = this.getLocalDateString(date);
3637
const entries = this.getEntries();
38+
3739
const existingEntryIndex = entries.findIndex(entry => entry.date === dateString && entry.project === project);
3840

3941
if (existingEntryIndex !== -1) {
40-
// Update existing entry
4142
entries[existingEntryIndex].timeSpent += timeSpent;
4243
} else {
43-
// Add new entry
4444
entries.push({ date: dateString, project, timeSpent });
4545
}
4646

47-
await this.context.globalState.update('timeEntries', entries);
47+
try {
48+
await this.updateEntries(entries);
49+
} catch (error) {
50+
console.error('Error saving entry:', error);
51+
vscode.window.showErrorMessage('Failed to save time entry');
52+
}
4853
}
4954

5055
getEntries(): TimeEntry[] {
51-
return this.context.globalState.get<TimeEntry[]>('timeEntries', []);
56+
if (!this.entries) {
57+
this.entries = this.context.globalState.get<TimeEntry[]>('timeEntries', []);
58+
}
59+
return this.entries;
60+
}
61+
62+
private async updateEntries(entries: TimeEntry[]): Promise<void> {
63+
this.entries = entries;
64+
await this.context.globalState.update('timeEntries', entries);
5265
}
5366

5467
async getSummaryData(): Promise<SummaryData> {
@@ -83,10 +96,10 @@ export class Database {
8396
const today = this.getLocalDateString(new Date());
8497
const entries = this.getEntries();
8598
const updatedEntries = entries.filter(entry => entry.date !== today);
86-
await this.context.globalState.update('timeEntries', updatedEntries);
99+
await this.updateEntries(updatedEntries);
87100
}
88101

89102
async resetAllTime(): Promise<void> {
90-
await this.context.globalState.update('timeEntries', []);
103+
await this.updateEntries([]);
91104
}
92105
}

src/extension.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@ export function activate(context: vscode.ExtensionContext) {
1717
})
1818
);
1919

20+
// Register configuration change listener
21+
context.subscriptions.push(
22+
vscode.workspace.onDidChangeConfiguration(e => {
23+
if (e.affectsConfiguration('simpleCodingTimeTracker')) {
24+
timeTracker.updateConfiguration();
25+
}
26+
})
27+
);
28+
2029
// Register the existing command
2130
let disposable = vscode.commands.registerCommand('simpleCodingTimeTracker.showSummary', () => {
2231
summaryView.show();
@@ -50,11 +59,28 @@ export function activate(context: vscode.ExtensionContext) {
5059
timeTracker.startTracking();
5160
}
5261

62+
// Variable to store the focus timeout handle
63+
let focusTimeoutHandle: NodeJS.Timeout | null = null;
64+
5365
vscode.window.onDidChangeWindowState((e: vscode.WindowState) => {
5466
if (e.focused) {
67+
if (focusTimeoutHandle) {
68+
clearTimeout(focusTimeoutHandle);
69+
focusTimeoutHandle = null;
70+
}
5571
timeTracker.startTracking();
5672
} else {
57-
timeTracker.stopTracking();
73+
const config = vscode.workspace.getConfiguration('simpleCodingTimeTracker');
74+
const focusTimeoutSeconds = config.get('focusTimeout', 60);
75+
76+
// Only stop tracking after the focus timeout
77+
if (focusTimeoutHandle) {
78+
clearTimeout(focusTimeoutHandle);
79+
}
80+
81+
focusTimeoutHandle = setTimeout(() => {
82+
timeTracker.stopTracking();
83+
}, focusTimeoutSeconds * 1000);
5884
}
5985
});
6086

src/timeTracker.ts

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,16 @@ export class TimeTracker implements vscode.Disposable {
77
private currentProject: string = '';
88
private database: Database;
99
private updateInterval: NodeJS.Timeout | null = null;
10-
private saveInterval: NodeJS.Timeout | null = null;
11-
private saveIntervalSeconds: number;
10+
private saveInterval: NodeJS.Timeout | null = null; private saveIntervalSeconds: number = 5;
1211
private lastCursorActivity: number = Date.now();
1312
private cursorInactivityTimeout: NodeJS.Timeout | null = null;
14-
private inactivityTimeoutSeconds: number;
13+
private inactivityTimeoutSeconds: number = 300;
14+
private focusTimeoutHandle: NodeJS.Timeout | null = null;
15+
private focusTimeoutSeconds: number = 60;
1516

1617
constructor(database: Database) {
1718
this.database = database;
18-
const config = vscode.workspace.getConfiguration('simpleCodingTimeTracker');
19-
this.saveIntervalSeconds = config.get('saveInterval', 5);
20-
this.inactivityTimeoutSeconds = config.get('inactivityTimeout', 300); // Default 5 minutes in seconds
19+
this.updateConfiguration();
2120

2221
// Track cursor movements
2322
vscode.window.onDidChangeTextEditorSelection(() => {
@@ -61,11 +60,33 @@ export class TimeTracker implements vscode.Disposable {
6160
// Track when VS Code window gains focus
6261
vscode.window.onDidChangeWindowState((e) => {
6362
if (e.focused) {
64-
this.updateCursorActivity();
63+
// Clear any pending timeout when focus returns
64+
if (this.focusTimeoutHandle) {
65+
clearTimeout(this.focusTimeoutHandle);
66+
this.focusTimeoutHandle = null;
67+
}
68+
this.startTracking();
69+
} else {
70+
// Set timeout when focus is lost
71+
if (this.focusTimeoutHandle) {
72+
clearTimeout(this.focusTimeoutHandle);
73+
}
74+
this.focusTimeoutHandle = setTimeout(() => {
75+
if (this.isTracking) {
76+
this.stopTracking();
77+
}
78+
}, this.focusTimeoutSeconds * 1000);
6579
}
6680
});
6781
}
6882

83+
public updateConfiguration() {
84+
const config = vscode.workspace.getConfiguration('simpleCodingTimeTracker');
85+
this.saveIntervalSeconds = config.get('saveInterval', 5);
86+
this.inactivityTimeoutSeconds = config.get('inactivityTimeout', 300); // Default 5 minutes in seconds
87+
this.focusTimeoutSeconds = config.get('focusTimeout', 60);
88+
}
89+
6990
private setupCursorTracking() {
7091
if (this.cursorInactivityTimeout) {
7192
clearTimeout(this.cursorInactivityTimeout);

0 commit comments

Comments
 (0)