Skip to content

Commit d5bd1bb

Browse files
committed
fix: resolve TypeScript and ESLint errors in extension and server
- Add AUTH/AUTH_SUCCESS to MessageType for WebSocket authentication - Implement missing TreeDataProvider methods in FriendsProvider - Fix exactOptionalPropertyTypes compatibility in buildActivityInfo - Install minimatch dependency, fix ESLint config extension - Remove async from sync handleHeartbeat function
1 parent 1b04e17 commit d5bd1bb

File tree

9 files changed

+88
-67
lines changed

9 files changed

+88
-67
lines changed

apps/extension/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,8 @@
223223
"clean": "rimraf dist out"
224224
},
225225
"dependencies": {
226-
"@devradar/shared": "workspace:*"
226+
"@devradar/shared": "workspace:*",
227+
"minimatch": "^10.1.1"
227228
},
228229
"devDependencies": {
229230
"@devradar/eslint-config": "workspace:*",

apps/extension/src/extension.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class DevRadarExtension implements vscode.Disposable {
4242
this.activityTracker = new ActivityTracker(this.wsClient, this.configManager, this.logger);
4343

4444
// Initialize views
45-
this.friendsProvider = new FriendsProvider(this.authService, this.configManager, this.logger);
45+
this.friendsProvider = new FriendsProvider(this.logger);
4646
this.activityProvider = new ActivityProvider(this.wsClient, this.logger);
4747
this.statusBar = new StatusBarManager(this.wsClient, this.authService, this.logger);
4848

@@ -330,9 +330,11 @@ class DevRadarExtension implements vscode.Disposable {
330330

331331
const trimmedMessage = message.trim();
332332
// Sanitize: strip control characters (except simple whitespace) and HTML-like tags
333+
/* eslint-disable no-control-regex */
333334
const sanitizedMessage = trimmedMessage
334335
.replace(/[\u0000-\u001f\u007f-\u009f]/g, '') // Remove control chars
335336
.replace(/<[^>]*>/g, ''); // Remove HTML tags
337+
/* eslint-enable no-control-regex */
336338

337339
this.wsClient.sendPoke(userId, sanitizedMessage);
338340
void vscode.window.showInformationMessage('DevRadar: Poke sent! 👋');

apps/extension/src/services/authService.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,11 @@ export class AuthService implements vscode.Disposable {
5959
},
6060
5 * 60 * 1000
6161
);
62-
this.disposables.push({ dispose: () => clearInterval(validationInterval) });
62+
this.disposables.push({
63+
dispose: () => {
64+
clearInterval(validationInterval);
65+
},
66+
});
6367
}
6468

6569
/**

apps/extension/src/utils/configManager.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
* Provides type-safe access to extension configuration with caching.
55
*/
66

7-
import * as vscode from 'vscode';
87
import { minimatch } from 'minimatch';
8+
import * as vscode from 'vscode';
99

1010
/**
1111
* Extension configuration schema.

apps/extension/src/views/friendsProvider.ts

Lines changed: 69 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,8 @@
66

77
import * as vscode from 'vscode';
88

9-
import type { AuthService } from '../services/authService';
10-
import type { ConfigManager } from '../utils/configManager';
11-
import type { WebSocketClient } from '../services/wsClient';
129
import type { Logger } from '../utils/logger';
13-
import type { UserDTO, UserStatus, UserStatusType } from '@devradar/shared';
10+
import type { ActivityPayload, UserDTO, UserStatus, UserStatusType } from '@devradar/shared';
1411

1512
/**
1613
* Friend item with status information.
@@ -221,19 +218,80 @@ export class FriendsProvider
221218
FriendTreeItem | GroupTreeItem | undefined
222219
>();
223220
private friends = new Map<string, FriendInfo>();
224-
private isAuthenticated = false;
225221

226222
readonly onDidChangeTreeData = this.onDidChangeTreeDataEmitter.event;
227223

228-
constructor(
229-
private readonly authService: AuthService,
230-
private readonly configManager: ConfigManager,
231-
private readonly logger: Logger
232-
) {
224+
constructor(private readonly logger: Logger) {
233225
this.disposables.push(this.onDidChangeTreeDataEmitter);
234226
}
235227

236-
// ... (keeping method order)
228+
/**
229+
* Returns a tree item for the given element.
230+
*/
231+
getTreeItem(element: FriendTreeItem | GroupTreeItem): vscode.TreeItem {
232+
return element;
233+
}
234+
235+
/**
236+
* Returns children for the given element or root elements if no element is provided.
237+
*/
238+
getChildren(element?: FriendTreeItem | GroupTreeItem): (FriendTreeItem | GroupTreeItem)[] {
239+
if (element instanceof GroupTreeItem) {
240+
// Return friends filtered by the group's status
241+
const statusGroup = element.statusGroup;
242+
const friendsList = Array.from(this.friends.values());
243+
244+
if (statusGroup === 'all') {
245+
return friendsList.map((friend) => new FriendTreeItem(friend));
246+
}
247+
248+
return friendsList
249+
.filter((friend) => friend.status === statusGroup)
250+
.map((friend) => new FriendTreeItem(friend));
251+
}
252+
253+
// Root level: return groups with counts
254+
const friendsList = Array.from(this.friends.values());
255+
const groups: GroupTreeItem[] = [];
256+
257+
const onlineCount = friendsList.filter((f) => f.status === 'online').length;
258+
const idleCount = friendsList.filter((f) => f.status === 'idle').length;
259+
const dndCount = friendsList.filter((f) => f.status === 'dnd').length;
260+
const offlineCount = friendsList.filter((f) => f.status === 'offline').length;
261+
262+
if (onlineCount > 0) groups.push(new GroupTreeItem('online', onlineCount));
263+
if (idleCount > 0) groups.push(new GroupTreeItem('idle', idleCount));
264+
if (dndCount > 0) groups.push(new GroupTreeItem('dnd', dndCount));
265+
if (offlineCount > 0) groups.push(new GroupTreeItem('offline', offlineCount));
266+
267+
return groups;
268+
}
269+
270+
/**
271+
* Refreshes the friends tree view.
272+
*/
273+
refresh(): void {
274+
this.onDidChangeTreeDataEmitter.fire(undefined);
275+
}
276+
277+
/**
278+
* Builds activity info from ActivityPayload.
279+
*/
280+
private buildActivityInfo(activity: ActivityPayload): FriendInfo['activity'] {
281+
const result: FriendInfo['activity'] = {
282+
sessionDuration: activity.sessionDuration,
283+
};
284+
if (activity.fileName !== undefined) {
285+
result.fileName = activity.fileName;
286+
}
287+
if (activity.language !== undefined) {
288+
result.language = activity.language;
289+
}
290+
if (activity.project !== undefined) {
291+
result.project = activity.project;
292+
}
293+
return result;
294+
}
237295

238296
/**
239297
* Handles friend status update from WebSocket.
@@ -284,55 +342,6 @@ export class FriendsProvider
284342
);
285343
}
286344

287-
/**
288-
* Fetches friends list from server.
289-
*/
290-
private async fetchFriends(): Promise<void> {
291-
try {
292-
const token = this.authService.getToken();
293-
if (!token) {
294-
return;
295-
}
296-
297-
this.logger.debug('Fetching friends list from server...');
298-
const serverUrl = this.configManager.get('serverUrl');
299-
300-
const response = await fetch(`${serverUrl}/api/v1/friends?limit=100`, {
301-
headers: {
302-
Authorization: `Bearer ${token}`,
303-
},
304-
});
305-
306-
if (!response.ok) {
307-
throw new Error(`Failed to fetch friends: ${String(response.status)}`);
308-
}
309-
310-
const json = (await response.json()) as { data: any[] };
311-
const friendsList = json.data;
312-
313-
this.friends.clear();
314-
315-
for (const friendData of friendsList) {
316-
const friend: FriendInfo = {
317-
id: friendData.id,
318-
username: friendData.username,
319-
displayName: friendData.displayName,
320-
avatarUrl: friendData.avatarUrl,
321-
status: friendData.status,
322-
activity: friendData.activity,
323-
lastUpdated: Date.now(),
324-
};
325-
this.friends.set(friend.id, friend);
326-
}
327-
328-
this.onDidChangeTreeDataEmitter.fire(undefined);
329-
this.logger.info(`Fetched ${String(this.friends.size)} friends`);
330-
} catch (error) {
331-
this.logger.error('Failed to fetch friends', error);
332-
void vscode.window.showErrorMessage('DevRadar: Failed to load friends list');
333-
}
334-
}
335-
336345
/**
337346
* Adds a friend to the list (called when receiving initial state).
338347
*/

apps/server/src/ws/handler.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,11 @@ async function unsubscribeFromFriends(ws: AuthenticatedWebSocket): Promise<void>
206206
/**
207207
* Handle heartbeat message.
208208
*/
209-
async function handleHeartbeat(
209+
function handleHeartbeat(
210210
ws: AuthenticatedWebSocket,
211211
payload: unknown,
212212
correlationId?: string
213-
): Promise<void> {
213+
): void {
214214
const result = HeartbeatMessageSchema.safeParse(payload);
215215

216216
if (!result.success) {
@@ -311,7 +311,7 @@ async function handleMessage(ws: AuthenticatedWebSocket, data: string): Promise<
311311

312312
switch (type) {
313313
case 'HEARTBEAT':
314-
await handleHeartbeat(ws, payload, correlationId);
314+
handleHeartbeat(ws, payload, correlationId);
315315
break;
316316

317317
case 'STATUS_UPDATE':

packages/shared/src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ export type RoleType = 'OWNER' | 'ADMIN' | 'MEMBER';
1717
* WebSocket message types.
1818
*/
1919
export type MessageType =
20+
| 'AUTH'
21+
| 'AUTH_SUCCESS'
2022
| 'STATUS_UPDATE'
2123
| 'FRIEND_STATUS'
2224
| 'POKE'

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)