Skip to content

Conversation

@senutpal
Copy link
Owner

@senutpal senutpal commented Jan 7, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Friend Requests view to manage incoming/outgoing requests with badge counts
    • Commands to add/search users, send, accept, reject, cancel requests, and focus the Friend Requests view
    • Context menu actions and an "Add Friend" control in the sidebar
    • Real-time notifications for request events
  • Bug Fixes

    • Unfriend now correctly removes bidirectional friendships
  • Chores

    • Extension updated to version 0.2.0

✏️ Tip: You can customize this high-level summary in your review settings.

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link

coderabbitai bot commented Jan 7, 2026

📝 Walkthrough

Walkthrough

Adds a full friend request system: client-side provider/service and commands in the VS Code extension, shared types/validators, server-side Prisma schema and REST routes with WebSocket events, and a pre-commit type check step.

Changes

Cohort / File(s) Summary
Pre-commit & Configuration
\.husky/pre-commit
Inserted pnpm check-types before pnpm lint-staged in pre-commit hook.
Extension Manifest
apps/extension/package.json
Bumped version to 0.2.0; added devradar.friendRequests view, friend-related commands (devradar.addFriend, devradar.acceptFriendRequest, devradar.rejectFriendRequest, devradar.cancelFriendRequest, devradar.refreshFriendRequests, devradar.unfriend, devradar.focusFriendRequests) and corresponding menu entries.
Extension Integration
apps/extension/src/extension.ts
Wired FriendRequestService and FriendRequestsProvider into extension lifecycle; registered tree view and commands; added WebSocket handlers for FRIEND_REQUEST_RECEIVED and FRIEND_REQUEST_ACCEPTED; implemented UI flows and handlers for add/accept/reject/cancel/unfriend/refresh/focus.
Extension Service
apps/extension/src/services/friendRequestService.ts
New exported FriendRequestService with search/send/get/accept/reject/cancel/unfriend methods, events (onRequestReceived, onRequestAccepted, onRequestsChanged), WebSocket event handlers, error logging, and dispose logic.
Service Exports
apps/extension/src/services/index.ts
Exported FriendRequestService.
Extension Views
apps/extension/src/views/friendRequestsProvider.ts
New FriendRequestsProvider implementing TreeDataProvider and Disposable, rendering incoming/outgoing sections, items with icons/tooltips, refresh/clear/getPendingCount methods.
View Exports
apps/extension/src/views/index.ts
Exported FriendRequestsProvider.
Server Schema
apps/server/prisma/schema.prisma
Added FriendRequest model and FriendRequestStatus enum; added sentFriendRequests and receivedFriendRequests relations on User; updated Team and TeamMember fields/constraints.
Server Routes
apps/server/src/routes/friendRequests.ts
New route set friendRequestRoutes(app) implementing POST / (send with duplicate/auto-accept logic), GET /incoming, GET /outgoing, POST /:id/accept, POST /:id/reject, DELETE /:id (cancel), GET /count, atomic accept transaction, DTO conversions, WebSocket broadcasts, and error handling.
Friends Route Update
apps/server/src/routes/friends.ts
Updated DELETE /friends/:id to remove bidirectional friendship (use OR find/deleteMany) and updated logging/message.
Server Registration
apps/server/src/server.ts
Registered friendRequestRoutes under /api/v1/friend-requests.
Shared Types
packages/shared/src/types.ts
Added FriendRequestStatus enum, extended MessageType with CONNECTED, FRIEND_REQUEST_RECEIVED, FRIEND_REQUEST_ACCEPTED, and added PublicUserDTO, FriendRequestDTO, FriendRequestReceivedPayload, FriendRequestAcceptedPayload.
Shared Validators
packages/shared/src/validators.ts
Added FriendRequestStatusSchema, SendFriendRequestSchema and inferred input types; extended MessageTypeSchema with new message types.
Changelog
apps/extension/CHANGELOG.md
Added 0.2.0 entry describing Friend Request features and UI updates.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant VSCode as VS Code Extension
    participant Service as FriendRequestService
    participant Server
    participant DB as Database
    participant WS as WebSocket

    User->>VSCode: Click "Add Friend"
    VSCode->>Service: searchUsers(query)
    Service->>Server: GET /api/users/search
    Server->>DB: Query users
    DB-->>Server: Users
    Server-->>Service: Users[]
    Service-->>VSCode: Display results
    User->>VSCode: Select user
    VSCode->>Service: sendRequest(toUserId)
    Service->>Server: POST /api/v1/friend-requests
    Server->>DB: Create FriendRequest (PENDING) / or detect reciprocal -> ACCEPTED
    alt New request
        Server->>WS: Broadcast FRIEND_REQUEST_RECEIVED to recipient
    else Reciprocal exists
        Server->>DB: Create bidirectional follow records
        Server->>WS: Broadcast FRIEND_REQUEST_ACCEPTED to both users
    end
    Server-->>Service: FriendRequestDTO/Success
    Service-->>VSCode: Emit onRequestsChanged / notify
    VSCode->>VSCode: Refresh friend requests & friends view
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is partially related to the changeset but is too vague and generic, lacking specific details about the implementation or scope of friend request features. Consider a more descriptive title such as 'Add friend request system with UI and backend support' or 'Implement friend request feature with notifications and unfriend capability' to better convey the scope of changes across extension, server, and shared packages.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/friend-requests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/server/src/routes/friends.ts (1)

238-281: Semantic inconsistency: DELETE endpoint removes bidirectional relationships, but POST creates only unidirectional ones.

The DELETE /friends/:id endpoint removes relationships in both directions (lines 267-275), but POST /friends/:id creates only unidirectional follows (lines 220-222). This creates an asymmetry:

  • POST /friends/:id: Creates A → B only
  • DELETE /friends/:id: Removes A → B AND B → A

If user A follows user B, and user B separately follows A, then A's DELETE request will remove B's follow relationship without B's consent. This violates user autonomy and could cause unintended data loss.

The DELETE endpoint's comment "removes bidirectional follow relationship" and log "Users unfriended (bidirectional)" also suggest a misalignment with the actual unidirectional semantics of the POST endpoint.

Recommendation: Clarify the intended model and choose one approach:

  1. Separate semantics: Keep /friends/:id for unidirectional follows; use a separate endpoint/model for bidirectional friendships (friend requests)
  2. Make POST bidirectional: Update POST to create relationships in both directions
  3. Make DELETE unidirectional: Only remove the specific direction created by the current user (A removes A → B only)
🤖 Fix all issues with AI agents
In @apps/extension/package.json:
- Around line 50-55: The view with id "devradar.friendRequests" references an
icon at "media/requests.svg" but that SVG is missing; fix by adding a new
requests.svg into the extension's media folder (matching the style/size of
existing icons like activity.svg/friends.svg) or update the view's "icon"
property to point to an existing asset; ensure the filename exactly matches
"media/requests.svg" and the SVG is valid and exported so the Friend Requests
view can load its icon.

In @apps/extension/src/extension.ts:
- Around line 137-168: The command 'devradar.focusFriendRequests' is registered
in extension.ts but not declared in apps/extension/package.json, so it won't
appear in the command palette; either add an entry for
'devradar.focusFriendRequests' under the package.json "contributes.commands"
array (provide an id, title and optional category) to make it user-accessible,
or if it is meant to be internal-only, add a clear comment next to the handler
in extension.ts documenting that intent and leave it out of package.json.
- Around line 615-655: handleUnfriend currently performs a raw fetch and
duplicates auth/HTTP logic and has a redundant response check; add an
unfriend(userId: string) method to FriendRequestService (or the appropriate
service) that handles building the URL, attaching the token (using
authService.getToken), performing the DELETE and throwing on non-ok responses,
then replace the inline fetch in handleUnfriend with a call to
FriendRequestService.unfriend(item.userId); keep the existing try/catch and
friendsProvider.refresh() flow, and if you opt to keep an inline fetch instead,
simplify the check to if (!response.ok) (remove the redundant response.status
!== 204 clause).

In @apps/extension/src/services/friendRequestService.ts:
- Around line 66-88: searchUsers (and other fetch callers in
friendRequestService.ts) can hang because there is no request timeout; add a
private helper like fetchWithTimeout that uses AbortController to abort after a
default timeout (e.g., 10_000 ms), ensure you pass the controller.signal into
fetch, clear the timeout in a finally block, and replace the direct fetch call
in searchUsers with this.fetchWithTimeout using the same headers and URL; apply
the same helper to all other fetch calls in this service so every network
request is bounded by the timeout.
- Around line 232-235: The condition checking `response.status !== 204` is
redundant because a 204 is already treated as success (`response.ok` true);
change the conditional from `if (!response.ok && response.status !== 204)` to
simply `if (!response.ok)` in the error-handling block (the code that reads
`const errorData = (await response.json()) as { error?: { message?: string } };
throw new Error(...)`) so the same error parsing/throwing logic executes only
when the response is not OK.

In @apps/server/prisma/schema.prisma:
- Around line 48-65: The FriendRequest model's @@unique([fromId, toId])
constraint prevents resending after a REJECTED request; remove or adjust that
uniqueness and implement cleanup or status-aware checks: either delete
REJECTED/EXPIRED rows before inserting a new FriendRequest (in the code path
that creates requests) or remove the @@unique([fromId, toId]) constraint and
enforce uniqueness in application logic by querying for existing PENDING
requests only; as an alternative you can change the schema to @@unique([fromId,
toId, status]) if you want uniqueness per status.

In @apps/server/src/routes/friendRequests.ts:
- Around line 132-144: The current check using db.follow.findFirst
(existingFollow) incorrectly treats a single-direction follow as "already
friends"; update the logic to assert mutual follows exist by checking for BOTH
follow records (followerId: userId → followingId: toUserId AND followerId:
toUserId → followingId: userId). Replace the findFirst with a query that
verifies both records (e.g., findMany with those two where clauses and ensure
count === 2, or perform two findUnique/findFirst calls) and only throw
ConflictError('You are already friends with this user') when both follow rows
are present.
- Around line 53-68: The file defines duplicate interfaces PublicUserDTO and
FriendRequestDTO; remove these local duplicates and import the canonical types
from the shared package (the types exported from packages/shared/src/types.ts),
then update the top-of-file imports to reference PublicUserDTO and
FriendRequestDTO instead of the local declarations and ensure any uses (e.g., in
route handlers or function signatures) keep the same names but rely on the
imported types.
- Around line 22-25: Remove the local SendFriendRequestSchema duplicate and
import the shared schema from the validators module instead; replace the local
const SendFriendRequestSchema declaration with an import of the existing
SendFriendRequestSchema from the shared validators (the same module that
provides PaginationQuerySchema) and update any references to use that imported
symbol.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28dadc7 and 2319c32.

📒 Files selected for processing (13)
  • .husky/pre-commit
  • apps/extension/package.json
  • apps/extension/src/extension.ts
  • apps/extension/src/services/friendRequestService.ts
  • apps/extension/src/services/index.ts
  • apps/extension/src/views/friendRequestsProvider.ts
  • apps/extension/src/views/index.ts
  • apps/server/prisma/schema.prisma
  • apps/server/src/routes/friendRequests.ts
  • apps/server/src/routes/friends.ts
  • apps/server/src/server.ts
  • packages/shared/src/types.ts
  • packages/shared/src/validators.ts
🧰 Additional context used
🧬 Code graph analysis (6)
apps/server/src/server.ts (1)
apps/server/src/routes/friendRequests.ts (1)
  • friendRequestRoutes (97-448)
apps/server/src/routes/friends.ts (2)
apps/server/src/lib/errors.ts (1)
  • NotFoundError (121-129)
apps/server/src/lib/logger.ts (1)
  • logger (72-72)
apps/extension/src/extension.ts (2)
apps/extension/src/views/friendRequestsProvider.ts (1)
  • FriendRequestsProvider (76-179)
packages/shared/src/types.ts (2)
  • FriendRequestDTO (101-108)
  • PublicUserDTO (93-98)
apps/server/src/routes/friendRequests.ts (6)
packages/shared/src/validators.ts (2)
  • SendFriendRequestSchema (81-83)
  • PaginationQuerySchema (75-78)
packages/shared/src/types.ts (2)
  • PublicUserDTO (93-98)
  • FriendRequestDTO (101-108)
apps/server/src/services/db.ts (1)
  • getDb (20-33)
apps/server/src/lib/errors.ts (4)
  • ValidationError (67-81)
  • ConflictError (134-145)
  • NotFoundError (121-129)
  • AuthorizationError (108-116)
apps/server/src/lib/logger.ts (1)
  • logger (72-72)
apps/server/src/ws/handler.ts (1)
  • broadcastToUsers (423-430)
apps/extension/src/services/friendRequestService.ts (2)
packages/shared/src/types.ts (3)
  • FriendRequestDTO (101-108)
  • PublicUserDTO (93-98)
  • PaginatedResponse (144-152)
apps/extension/src/utils/logger.ts (1)
  • error (51-59)
apps/extension/src/views/friendRequestsProvider.ts (4)
packages/shared/src/types.ts (1)
  • FriendRequestDTO (101-108)
apps/extension/src/views/index.ts (1)
  • FriendRequestsProvider (2-2)
apps/server/src/lib/logger.ts (1)
  • logger (72-72)
apps/extension/src/utils/logger.ts (1)
  • error (51-59)
🔇 Additional comments (37)
.husky/pre-commit (1)

1-1: LGTM! Good addition of type checking.

Adding type checking before lint-staged is a best practice that helps catch type errors early in the development workflow.

packages/shared/src/types.ts (1)

10-11: LGTM! Well-structured type definitions.

The new types for friend request functionality are well-defined:

  • FriendRequestStatus provides clear status values
  • PublicUserDTO appropriately limits exposed user information
  • FriendRequestDTO and payload interfaces are properly structured for the friend request workflow
  • New MessageType values support real-time notifications

Also applies to: 17-17, 25-27, 92-119

packages/shared/src/validators.ts (1)

12-13: LGTM! Validators align with type definitions.

The new validation schemas properly support the friend request functionality:

  • FriendRequestStatusSchema matches the FriendRequestStatus type
  • MessageTypeSchema includes all new WebSocket message types
  • SendFriendRequestSchema validates the friend request payload with appropriate constraints

Also applies to: 25-27, 80-94

apps/server/prisma/schema.prisma (2)

28-29: LGTM! User model relations properly configured.

The new sentFriendRequests and receivedFriendRequests relations are correctly defined and will support bidirectional friend request queries.


84-96: LGTM! Good schema improvements.

The changes improve data integrity and query performance:

  • TeamMember now has a unique constraint on [userId, teamId] preventing duplicate memberships
  • Indexes on userId and teamId will improve query performance
  • FriendRequestStatus enum is well-defined

Also applies to: 110-114

apps/server/src/routes/friendRequests.ts (6)

212-292: LGTM!

The incoming and outgoing request endpoints correctly implement pagination with sensible defaults, include related user data, and calculate hasMore accurately.


294-338: LGTM!

The accept endpoint properly validates the request ID, verifies ownership (only the recipient can accept), ensures the request is still pending, and delegates to the internal helper for atomic processing.


340-385: LGTM!

The reject endpoint correctly validates authorization and status before updating. Keeping rejected requests (rather than deleting) allows for audit trails.


387-429: LGTM!

The cancel endpoint correctly restricts the action to the sender and properly deletes the request with a 204 response.


431-448: LGTM!

Simple and efficient count endpoint for badge display purposes.


450-494: LGTM!

The internal helper properly uses a transaction for atomicity, handles potential duplicate follows gracefully with skipDuplicates, and broadcasts acceptance events to both parties after the transaction commits.

apps/server/src/server.ts (2)

25-25: LGTM!

The import is correctly placed and follows the existing naming convention.


217-217: LGTM!

The route registration follows the established pattern and the /friend-requests prefix appropriately aligns with the route module's endpoint design.

apps/extension/package.json (2)

114-149: LGTM!

The new commands are well-structured with appropriate icons and categorization. The naming is consistent with existing commands.


157-197: LGTM!

Menu configurations correctly use when clauses to scope actions:

  • Accept/reject only shown for incoming requests (friendRequest-incoming)
  • Cancel only shown for outgoing requests (friendRequest-outgoing)
  • Unfriend scoped to friend items with viewItem =~ /^friend-/
  • Inline grouping with @1 and @2 ensures consistent action ordering
apps/extension/src/services/index.ts (1)

4-4: LGTM!

The barrel export follows the established pattern for exposing services.

apps/extension/src/views/index.ts (1)

2-2: LGTM!

The barrel export follows the established pattern for exposing view providers.

apps/extension/src/views/friendRequestsProvider.ts (6)

12-51: LGTM!

The FriendRequestTreeItem class properly displays user info based on direction, uses appropriate theme icons with colors, and builds informative tooltips with Markdown.


53-71: LGTM!

The SectionTreeItem class provides clear section headers with counts and appropriate initial expansion state (expanded if items exist).


75-101: LGTM!

The provider constructor properly initializes resources and subscribes to the service's change events for automatic refresh. Disposables are correctly tracked.


103-130: LGTM!

The tree data methods correctly implement the two-level hierarchy: sections at root level and request items as children.


132-160: LGTM!

The refresh method properly:

  • Prevents concurrent refreshes with isLoading guard
  • Fetches data in parallel with Promise.all
  • Preserves existing data on error
  • Always fires the change event in finally

162-179: LGTM!

The utility methods (clear, getPendingCount, dispose) are straightforward and correctly implemented.

apps/extension/src/extension.ts (8)

10-11: LGTM!

The imports for FriendRequestService and FriendRequestsProvider are correctly added.


42-66: LGTM!

The service and provider initialization follows the established pattern, and disposables are correctly tracked for cleanup.


94-99: LGTM!

The friend requests tree view is correctly registered alongside existing views.


224-235: LGTM!

WebSocket message handling for friend request events is correctly implemented, with appropriate type casting and triggering a friends list refresh on acceptance.


260-262: LGTM!

Conditionally refreshing friend requests only when authenticated prevents unnecessary network calls.


480-540: LGTM!

The handleAddFriend flow is well-structured with proper input validation, user search, filtering, and error handling.


542-555: LGTM!

The isFriendRequestItem type guard thoroughly validates the nested structure.


557-608: LGTM!

The accept, reject, and cancel handlers follow a consistent pattern with proper validation, service calls, user feedback, and error handling. The accept handler correctly refreshes the friends list.

apps/extension/src/services/friendRequestService.ts (6)

1-22: LGTM!

Clean imports and well-defined event interface that properly types the three event streams exposed by the service.


24-49: LGTM!

Good dependency injection and proper disposal pattern. EventEmitters are correctly tracked in the disposables array for cleanup.


51-63: LGTM!

Authentication enforcement is properly implemented by throwing when no token is available.


117-157: LGTM!

Pagination is correctly implemented with sensible defaults. The same timeout considerations flagged earlier apply here.


159-176: LGTM!

Returning 0 on error is appropriate for a badge count - graceful degradation without disrupting the UI.


263-278: LGTM!

Event handler and disposal logic are correctly implemented.

…nd method

- Add fetchWithTimeout helper with 10s default timeout using AbortController
- Add unfriend() method to FriendRequestService
- Improve error handling for JSON parsing in error responses
- Fix notification error handling with proper catch and user feedback
- Import SendFriendRequestSchema from @devradar/shared instead of local duplicate
- Import PublicUserDTO and FriendRequestDTO types from @devradar/shared
- Fix 'already friends' check to verify mutual follows (both directions)
- Add cleanup of REJECTED requests before creating new ones
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @apps/server/src/routes/friendRequests.ts:
- Around line 162-170: The existence check for the authenticated user (the
db.user.findUnique call that assigns currentUser and the subsequent
NotFoundError throw) must be moved earlier in the handler to run before any
state-changing operations (e.g., before deleting rejected requests) so we never
mutate data for a user that no longer exists; locate the currentUser variable
and NotFoundError usage in the friend requests route/handler and move that check
to run immediately after authentication (or alternatively remove the redundant
check if you can guarantee authentication implies existence), ensuring all
references to currentUser still point to the validated result.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2319c32 and 5789049.

⛔ Files ignored due to path filters (1)
  • apps/extension/media/requests.svg is excluded by !**/*.svg
📒 Files selected for processing (5)
  • apps/extension/CHANGELOG.md
  • apps/extension/package.json
  • apps/extension/src/extension.ts
  • apps/extension/src/services/friendRequestService.ts
  • apps/server/src/routes/friendRequests.ts
🧰 Additional context used
🧬 Code graph analysis (2)
apps/server/src/routes/friendRequests.ts (6)
packages/shared/src/types.ts (2)
  • PublicUserDTO (93-98)
  • FriendRequestDTO (101-108)
apps/server/src/services/db.ts (1)
  • getDb (20-33)
packages/shared/src/validators.ts (2)
  • SendFriendRequestSchema (81-83)
  • PaginationQuerySchema (75-78)
apps/server/src/lib/errors.ts (4)
  • ValidationError (67-81)
  • ConflictError (134-145)
  • NotFoundError (121-129)
  • AuthorizationError (108-116)
apps/server/src/lib/logger.ts (1)
  • logger (72-72)
apps/server/src/ws/handler.ts (1)
  • broadcastToUsers (423-430)
apps/extension/src/services/friendRequestService.ts (3)
packages/shared/src/types.ts (3)
  • FriendRequestDTO (101-108)
  • PublicUserDTO (93-98)
  • PaginatedResponse (144-152)
apps/extension/src/services/index.ts (1)
  • FriendRequestService (4-4)
apps/extension/src/utils/logger.ts (1)
  • error (51-59)
🔇 Additional comments (24)
apps/extension/CHANGELOG.md (1)

8-24: LGTM!

The changelog entry is well-structured, follows the Keep a Changelog format, and comprehensively documents the new friend request system features.

apps/server/src/routes/friendRequests.ts (5)

1-27: LGTM!

The imports, schema definitions, and type interfaces are well-organized. The RequestIdParamsSchema provides proper validation for route parameters.


49-73: LGTM!

The DTO conversion functions are clean and correctly transform database entities to API response shapes.


200-280: LGTM!

The incoming and outgoing request endpoints are well-implemented with proper pagination, parallel database queries, and consistent response structure.


282-417: LGTM!

The accept, reject, and cancel routes have proper authorization checks, status validation, and appropriate error handling. The distinction between recipient-only operations (accept/reject) and sender-only operations (cancel) is correctly enforced.


438-482: LGTM!

The acceptRequestInternal function correctly uses a transaction for atomicity, creates bidirectional follow entries with skipDuplicates for safety, and broadcasts acceptance events to both users with appropriate friend information.

apps/extension/src/extension.ts (6)

10-11: LGTM!

The new service and provider are properly imported, declared, initialized, and added to disposables for proper lifecycle management.

Also applies to: 30-31, 42-52


94-97: LGTM!

The tree view registration and new command handlers are correctly wired up. The command structure follows the existing pattern.

Also applies to: 137-168


224-235: LGTM!

The WebSocket message handlers correctly process friend request events, delegate to the service for notifications, and refresh the friends list when friendships are established.


480-540: LGTM!

The handleAddFriend method has good UX flow with validation, filtering out the current user, and proper error handling with user-friendly messages.


542-555: LGTM!

The isFriendRequestItem type guard is thorough with proper null checks and property existence verification.


557-642: LGTM!

The friend request action handlers (accept, reject, cancel, unfriend) follow consistent patterns with proper type checking, error handling, and appropriate UI feedback. The unfriend action includes a confirmation dialog which is good UX for a destructive operation.

apps/extension/package.json (4)

5-5: LGTM!

Version bump to 0.2.0 aligns with the new feature release documented in the changelog.


114-154: LGTM!

The new commands are well-defined with appropriate titles, categories, and icons using VS Code's built-in product icon set.


162-202: LGTM!

The menu contributions are correctly configured with appropriate when clauses to ensure actions appear in the right context. The inline grouping for accept/reject buttons and the regex pattern for unfriend (viewItem =~ /^friend-/) are well thought out.


50-55: No action required—icon file exists.

The requests.svg file is present at apps/extension/media/requests.svg.

apps/extension/src/services/friendRequestService.ts (8)

17-49: LGTM!

The event emitter setup is well-structured with proper typing and the emitters are correctly added to disposables for cleanup.


51-88: LGTM!

The helper methods are well-implemented. The fetchWithTimeout correctly uses AbortController with proper cleanup in the finally block to prevent memory leaks.


90-114: LGTM!

The searchUsers method correctly validates input length, encodes the query parameter, and handles errors appropriately.


116-151: LGTM!

The sendRequest method has proper error extraction from server responses and fires the change event on success.


153-215: LGTM!

The pagination methods (getIncomingRequests, getOutgoingRequests) and getPendingCount are correctly implemented. The silent fallback to 0 for getPendingCount is appropriate for a badge display feature.


217-308: LGTM!

The accept, reject, and cancel methods have consistent error handling patterns and properly account for 204 No Content responses on DELETE operations.


310-341: LGTM!

The WebSocket event handlers provide excellent UX with actionable notifications. The inline accept action from the notification is a nice touch with proper error handling.


343-376: LGTM!

The unfriend method correctly uses the /friends/{userId} endpoint and handles the 204 response appropriately. The dispose method properly cleans up all event emitters.

Comment on lines +162 to +170
/* Get current user info for the response */
const currentUser = await db.user.findUnique({
where: { id: userId },
select: { id: true, username: true, displayName: true, avatarUrl: true },
});

if (!currentUser) {
throw new NotFoundError('User', userId);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider reordering the current user check.

The current user existence check happens after deleting rejected requests (lines 154-160). While unlikely, if the authenticated user was somehow deleted between authentication and this point, you'd throw NotFoundError after already modifying data. Consider moving this check earlier or removing it since the user is already authenticated.

♻️ Suggested improvement
+      /* Get current user info for the response - do this early */
+      const currentUser = await db.user.findUnique({
+        where: { id: userId },
+        select: { id: true, username: true, displayName: true, avatarUrl: true },
+      });
+
+      if (!currentUser) {
+        throw new NotFoundError('User', userId);
+      }
+
       /* Delete any previously REJECTED requests to allow resending */
       await db.friendRequest.deleteMany({
         where: {
           fromId: userId,
           toId: toUserId,
           status: 'REJECTED',
         },
       });

-      /* Get current user info for the response */
-      const currentUser = await db.user.findUnique({
-        where: { id: userId },
-        select: { id: true, username: true, displayName: true, avatarUrl: true },
-      });
-
-      if (!currentUser) {
-        throw new NotFoundError('User', userId);
-      }
-
       /* Create the friend request */

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @apps/server/src/routes/friendRequests.ts around lines 162 - 170, The
existence check for the authenticated user (the db.user.findUnique call that
assigns currentUser and the subsequent NotFoundError throw) must be moved
earlier in the handler to run before any state-changing operations (e.g., before
deleting rejected requests) so we never mutate data for a user that no longer
exists; locate the currentUser variable and NotFoundError usage in the friend
requests route/handler and move that check to run immediately after
authentication (or alternatively remove the redundant check if you can guarantee
authentication implies existence), ensuring all references to currentUser still
point to the validated result.

@senutpal senutpal merged commit c785e20 into main Jan 8, 2026
4 checks passed
@senutpal senutpal deleted the feature/friend-requests branch January 8, 2026 05:50
@senutpal senutpal restored the feature/friend-requests branch January 8, 2026 11:03
@senutpal senutpal deleted the feature/friend-requests branch January 11, 2026 13:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant