Skip to content

feat: friends panel#3990

Draft
evanpelle wants to merge 1 commit into
mainfrom
friends
Draft

feat: friends panel#3990
evanpelle wants to merge 1 commit into
mainfrom
friends

Conversation

@evanpelle
Copy link
Copy Markdown
Collaborator

@evanpelle evanpelle commented May 22, 2026

Description:

Add Friends tab to Account modal

Summary

  • Adds a "Friends" tab to the Account modal, alongside Account / Stats / Games.
  • New <friends-list> Lit component covering the full friend lifecycle: send request, accept / deny incoming, withdraw outgoing, remove friend, paginated list with "Load more".
  • New FriendsApi.ts wrapping GET/POST/DELETE /friends* endpoints with typed error codes (not_found / conflict / bad_request / request_failed).
  • Zod schemas for the friend API responses in core/ApiSchemas.ts.
  • Translations under a new friends.* block in en.json.

Friends and pending requests are displayed by public ID via copy-button, matching the existing clan convention.

Please complete the following:

  • I have added screenshots for all UI updates
  • I process any text displayed to the user through translateText() and I've added it to the en.json file
  • I have added relevant tests to the test directory
  • I confirm I have thoroughly tested these changes and take full responsibility for any bugs introduced

Please put your Discord username so you can be contacted if a bug or regression is found:

evan

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

Review Change Stack

Walkthrough

This PR introduces a complete friend management system. It defines API schemas and types, provides client-side fetch functions for friend operations, implements a FriendsList component that displays and manages friends and requests, and integrates the component into AccountModal with translation strings.

Changes

Friends Feature Implementation

Layer / File(s) Summary
Friend API Schemas and Types
src/core/ApiSchemas.ts
Exports Zod schemas and inferred types for friend entries (public ID and creation date), friend requests (incoming and outgoing lists), paginated friend list responses, and friend request status (requested or accepted).
Friends API Client
src/client/FriendsApi.ts
Exports a FriendActionError union type and seven functions: fetchFriendRequests(), fetchFriends(page, limit), sendFriendRequest(), acceptFriendRequest(), deleteFriendRequest(), and removeFriend(). All use a shared friendsFetch() helper that injects authorization and validates responses with Zod, returning false on network failure or parsing errors, and mapping HTTP status codes to error strings.
FriendsList Web Component
src/client/components/FriendsList.ts
Implements a LitElement custom element that loads friends and pending requests on connection, provides actions to send/accept/deny/withdraw/remove with optimistic state updates and error toasts, supports paginated loading of additional friends, and renders sections for adding new friends, viewing pending requests, and listing accepted friends.
AccountModal Integration and Translations
src/client/AccountModal.ts, resources/lang/en.json
Adds a new friends tab to AccountModal that renders the FriendsList component with the user's public ID, and provides translation keys for the tab label, input prompts, action buttons, request states, loading/empty states, and localized error messages.

Sequence Diagram

sequenceDiagram
  participant User
  participant FriendsList
  participant FriendsApi
  participant Server
  User->>FriendsList: Load friends tab
  FriendsList->>FriendsApi: fetchFriendRequests()
  FriendsList->>FriendsApi: fetchFriends(page, limit)
  FriendsApi->>Server: GET /friends/requests
  FriendsApi->>Server: GET /friends?page=X&limit=Y
  Server-->>FriendsApi: incoming/outgoing requests
  Server-->>FriendsApi: paginated friends list
  FriendsList->>FriendsList: render lists and actions
  User->>FriendsList: send friend request
  FriendsList->>FriendsApi: sendFriendRequest(publicId)
  FriendsApi->>Server: POST /friends/requests/{publicId}
  Server-->>FriendsApi: status (requested|accepted)
  FriendsApi-->>FriendsList: response or error
  FriendsList->>FriendsList: update state, show toast
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • openfrontio/OpenFrontIO#3986: Both PRs extend src/client/AccountModal.ts to support dynamic tab rendering with translation-keyed labels, making this PR's new friends tab a direct continuation of the same tab infrastructure.

Poem

🤝 Friends on the network, requests in the queue,
Send and accept, deny when you choose,
Pagination flows through the streams,
Making connections from code and dreams!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description clearly outlines the implementation of a Friends feature, including new components, API wrapper, schemas, and translations that match the changeset.
Title check ✅ Passed The title 'feat: friends panel' directly corresponds to the main change—adding a new friends feature with a panel/tab in the account modal and supporting components.

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


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
Copy Markdown
Contributor

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/client/components/FriendsList.ts`:
- Around line 117-123: After mutating local lists (e.g., this.incoming,
this.friends, this.friendsTotal) in the accept/remove handlers, reset the
component's pagination cache/state and re-fetch the first page instead of only
editing the local arrays to avoid later duplicate/missing rows; specifically,
after the local update (the block that modifies this.incoming, this.friends and
this.friendsTotal), clear whatever pagination buffers you maintain (e.g., pages
array, nextCursor/currentPage) and invoke loadMore() (or a refreshFriends()
helper) to reload from the server so the client and paginated server state stay
in sync.

In `@src/core/ApiSchemas.ts`:
- Around line 221-248: Add unit tests that validate FriendEntrySchema,
FriendRequestsResponseSchema, FriendsListResponseSchema, and
SendFriendRequestResponseSchema: for FriendEntrySchema test a valid object with
publicId string and createdAt ISO datetime and invalid cases (missing publicId,
bad createdAt); for FriendRequestsResponseSchema test valid payloads where
incoming/outgoing are arrays of valid FriendEntry objects and invalid cases
(non-array, invalid entries); for FriendsListResponseSchema test a valid payload
with results array of FriendEntry objects and numeric total/page/limit and
invalid cases (missing/negative/non-numeric pagination fields, non-array
results, invalid entries); and for SendFriendRequestResponseSchema test accepted
valid enum values "requested" and "accepted" plus invalid string values. Use the
schema parse/validation methods in your test harness to assert success for valid
fixtures and thrown/failed validation for each invalid fixture, referencing the
schema symbols FriendEntrySchema, FriendRequestsResponseSchema,
FriendsListResponseSchema, and SendFriendRequestResponseSchema to locate the
code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a87f5d49-6e0e-4a30-828b-bc458b146182

📥 Commits

Reviewing files that changed from the base of the PR and between b486caa and c0ef9b0.

📒 Files selected for processing (5)
  • resources/lang/en.json
  • src/client/AccountModal.ts
  • src/client/FriendsApi.ts
  • src/client/components/FriendsList.ts
  • src/core/ApiSchemas.ts

Comment on lines +117 to +123
this.incoming = this.incoming.filter((r) => r.publicId !== publicId);
this.friends = [
{ publicId, createdAt: new Date().toISOString() },
...this.friends,
];
this.friendsTotal++;
showToast(translateText("friends.request_accepted"), "green");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Re-sync paginated friends after local accept/remove updates.

On Line 117 and Line 166, local list edits can desync pagination state, which can later produce duplicate/missing rows when loadMore() requests the next server page.

Suggested fix
   private async handleAccept(publicId: string): Promise<void> {
@@
       if (result !== true) {
         showToast(translateText(this.errorKey(result)), "red");
         return;
       }
       this.incoming = this.incoming.filter((r) => r.publicId !== publicId);
-      this.friends = [
-        { publicId, createdAt: new Date().toISOString() },
-        ...this.friends,
-      ];
-      this.friendsTotal++;
+      await this.loadAll();
       showToast(translateText("friends.request_accepted"), "green");
@@
   private async handleRemove(publicId: string): Promise<void> {
@@
       if (result !== true) {
         showToast(translateText(this.errorKey(result)), "red");
         return;
       }
-      this.friends = this.friends.filter((f) => f.publicId !== publicId);
-      this.friendsTotal = Math.max(0, this.friendsTotal - 1);
+      await this.loadAll();
       showToast(translateText("friends.friend_removed"), "green");

Also applies to: 166-167

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/client/components/FriendsList.ts` around lines 117 - 123, After mutating
local lists (e.g., this.incoming, this.friends, this.friendsTotal) in the
accept/remove handlers, reset the component's pagination cache/state and
re-fetch the first page instead of only editing the local arrays to avoid later
duplicate/missing rows; specifically, after the local update (the block that
modifies this.incoming, this.friends and this.friendsTotal), clear whatever
pagination buffers you maintain (e.g., pages array, nextCursor/currentPage) and
invoke loadMore() (or a refreshFriends() helper) to reload from the server so
the client and paginated server state stay in sync.

Comment thread src/core/ApiSchemas.ts
@github-project-automation github-project-automation Bot moved this from Triage to Development in OpenFront Release Management May 22, 2026
@evanpelle evanpelle changed the title friends feat: friends panel May 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Development

Development

Successfully merging this pull request may close these issues.

1 participant