Add a typed in-app notification inbox for social actions
- Dominant language
- JavaScript
- Stars
- 30
- Forks
- 9
- PR merge metrics
- No merged PRs in 30d
Description
## Goal
Provide a durable in-app inbox for Friend System actions, with realtime delivery as an enhancement rather than the source of truth.
## MVP notification types
The notification foundation ships first with:
- incoming friend request;
- friend request accepted.
It must define the typed extension point that #187 uses for room invitations, but #105 does not wait for the room-invitation resource to be implemented.
Friend presence is shown in the friends hub and does not create a durable "came online" notification. The MVP does not send email, SMS, browser push, or OS notifications.
## Safety model
Notifications are typed references to server-owned resources, not arbitrary action definitions, callback URLs, or executable payloads. Each action calls the authenticated relationship/invitation endpoint, which revalidates the current resource state and recipient. A stale notification may become read/non-actionable but can never authorize an operation by itself.
Never ingest, address, search, deliver, or render notifications through email.
## Data and delivery
- Persist recipient, type, actor reference, source type/ID, dedupe key, created/read timestamps, and expiry/retention metadata.
- MongoDB remains the source of truth; add a backward-compatible PostgreSQL mirror without switching reads.
- Deduplicate repeated events for the same active source.
- Send a typed `notification_created/updated` event to the recipient's existing per-user Socket.IO room.
- Refetch unread count/recent notifications on connect and reconnect so Redis interruption or missed events self-heal.
- Expired/resolved resources remain safe to display but have no invalid action.
## User experience
- Header bell with unread count and a compact recent-notification menu.
- Full `/notifications` page with cursor pagination.
- Mark one/all as read.
- Accept/decline friend requests with pending, success, stale, and error states.
- Provide a typed renderer/action registry that #187 can extend with join/decline room-invite actions without accepting arbitrary server-defined callbacks.
- Keyboard-accessible actions and responsive mobile layout.
## Acceptance criteria
- [ ] Define the typed schema, dedupe rules, retention, and source-resource contract.
- [ ] Add authenticated cursor-paginated list/unread/read APIs with recipient-only authorization.
- [ ] Create friend-request/acceptance notifications idempotently from #75 state transitions.
- [ ] Add realtime delivery plus reconnect reconciliation through Redux middleware.
- [ ] Add header dropdown, badge, full page, empty/loading/error states, and typed friend actions.
- [ ] Add a safe typed extension seam for #187's room-invitation notification.
- [ ] Ensure notification payloads never contain access tokens, email, room passwords, private room membership lists, or arbitrary URLs/actions.
- [ ] Add server tests for recipient isolation, pagination, dedupe, stale sources, replay, expiry, read state, and disabled PostgreSQL.
- [ ] Add client tests for unread counts, action races, reconnect, unknown types, accessibility, and mobile layout.
- [ ] Add a two-user Cypress friend-request notification flow.
## Dependencies
- #75 for friend request actions
- #191 for the no-email invariant
## Launch gate
- #188 must pass before broad rollout; it does not block building this foundation.
## Follow-up integration
- #187 adds the room invitation resource and its join/decline notification actions after this foundation exists.
## Non-goals
- Email notifications.
- Browser/OS push.
- Direct messages.
- Durable friend-online alerts.
Contributor guide
Assessment
This issue has not been assessed yet.