| Attribute | Value |
|---|---|
| Category | IV.1 |
| Type | Major |
| Points | 2 |
| Status | Done |
| Developers | Busedame (friends backend, invite system, sort), AudreyBil (friends frontend, event attend/unattend), johdac (direct messages, online status), dovy-mus (public profiles, friendship status API) |
A comprehensive social interaction system: users can view each other's public profiles, manage friendships, attend events together, invite friends to events, initiate direct messages, and see which friends are online.
A social event platform only becomes social when users can find and connect with each other. This module brings the "social" into GRIT — without it, the app is a solitary event listing tool. It enables the network effect: discovering events through friends, coordinating attendance, and communicating in context.
Endpoints:
GET /users/:id— Returns public profile data (name, avatar, bio, city, country, hosted events)GET /users/:id/events— Returns published events hosted by this user
Privacy rules:
isProfilePublic = true(default): visible to all authenticated users.isProfilePublic = false: visible only to self and confirmed friends.- Non-friends accessing a private profile receive
404(not403) to prevent user enumeration.
Endpoint: GET /users?search=<query>&limit=<n>&cursor=<cursor>
Keyword search with cursor-based pagination. Used in:
- The Friends page search box.
- The Global search (⌘K) to show up to 5 matching users.
model FriendRequest {
id String @id @default(uuid())
requesterId Int
receiverId Int
requester User @relation("requester", ...)
receiver User @relation("receiver", ...)
createdAt DateTime @default(now())
@@unique([requesterId, receiverId])
}
model Friends {
id String @id @default(uuid())
userId Int
friendId Int
user User @relation("FriendUserA", ...)
friend User @relation("FriendUserB", ...)
createdAt DateTime @default(now())
@@unique([userId, friendId])
}- Send request — Creates a
FriendRequestrecord. - Accept — Creates a
Friendsrecord; deletes theFriendRequest. - Decline — Deletes the
FriendRequest. - Cancel (by sender) — Sender retracts a pending request.
- Remove friend — Deletes the
Friendsrecord.
Endpoint: GET /users/me/friends/status/:userId
Returns one of: none | pending_sent | pending_received | friends | self
Powers the action buttons on public profile pages and user cards in the friends search results.
Three sections driven by three parallel loader calls:
- Friend search: debounced 500ms input, filters out existing friends and the current user.
- Pending requests: incoming requests with Accept / Decline buttons.
- Your friends: confirmed friends with Chat and Remove buttons; sort button (A→Z / Z→A) to order alphabetically.
All actions call revalidate() after completing. A 30-second polling interval (useRevalidator) provides live updates.
See Module 05 — Advanced Permissions System for permission rules, and the Features List in README for the full feature description.
enum InviteStatus { PENDING ACCEPTED DECLINED }
model EventInvite {
id String @id @default(uuid())
senderId Int
receiverId Int
eventId Int
status InviteStatus @default(PENDING)
sender User @relation("InviteSender", ...)
receiver User @relation("InviteReceiver", ...)
event Event @relation(...)
createdAt DateTime @default(now())
}- Author (or any user on public events) opens the "Invite" button on the single event page.
- A searchable list of friends appears with per-friend statuses: Invite / Invited / Already going.
- Clicking "Invite" sends a
POST /event-invitesrequest, creating theEventInviterecord and automatically sending a chat message with a link to the event. - The invited user sees the event in their Invitations tab (My Events page).
- On the single event page, invited users see an Accept / Decline dropdown.
- Accepting attendance removes the invite and adds the user to
EventAttendee. - The invite is automatically cascade-deleted when: the user or event is deleted, or the invite is accepted/declined.
Endpoint: PATCH /users/me with { attending: eventId } / { unattending: eventId }
When a user marks themselves as attending:
- Added to
EventAttendeejoin table. - Automatically added to the event's
Conversationas a participant. - WebSocket synced to the event's chat room.
- Any pending
EventInvitefor that event is deleted.
Event cards show avatars of attending friends plus a count. Computed in the frontend by cross-referencing the event's attendees with the current user's full friends list (fetched via repeated paginated API calls if needed).
The ChatGateway maintains a userId → socketId map. The friends overview queries this map to show a live presence indicator next to each friend's name.
When a user is deleted:
- Friend requests and friendships deleted.
- Event invites (sent and received) deleted.
- Conversation participations deleted.
- Chat messages:
authorIdset to null (message content preserved as "unknown"). - Event attendance deleted.