feat(activity): activity center at /activity (LC-82) #142

Merged
nrupard merged 1 commit from feat/lc-82-activity into main 2026-05-18 20:16:15 +02:00
Owner

Summary

LC-82: GET /activity lists every event affecting the viewer across rooms - mentions of them, replies to threads they authored, reactions on messages they authored. On-demand UNION over the existing mentions, messages, and message_reactions tables; no new tables per the issue's "ship the cheap write path first" recommendation.

Routes / UX

  • GET /activity - All tab.
  • GET /activity?tab=mention|reply|reaction - filtered.
  • Tabs in header; sidebar nav gains an "Activity" link beside Inbox / Saved.
  • Each row deep-links to /{room|dm}/{id}#msg-{message_id}.
  • Empty state: "Nothing to see here. New mentions, replies, and reactions will show up as they happen."

Query shape

Three SELECTs (one per kind), merged in Rust by created_at DESC, capped at 60 rows total. Each scopes to rooms the viewer can currently see (r.room_type = 'public' OR EXISTS room_members rm WHERE rm.user_id = ?) so messages from rooms the viewer has since left are hidden (LC-82 AC #6). Self-authored events suppressed (m.user_id != ? / mr.user_id != ?).

Acceptance criteria

  • /activity lists mentions, replies, reactions in time order across rooms the user can see.
  • Tabs filter by kind.
  • Clicking a row navigates to the source message (deep-link with #msg-N). Mark-read in source room is the existing flow on room open.
  • Sidebar unread badge - deferred (needs per-item read state).
  • Mark-all-as-read - deferred (needs per-item read state).
  • Activity from rooms the user has left is hidden (access check in WHERE clause).

Stack

Independent of the unmerged stack (feat/lc-81-jump-and-inbox etc are now merged). Branches off current main.

Test plan

  • just check.
  • ./dev/cargo test -p lets-chat-server --test routes_activity - 2/2 pass (empty state, peer mention appears).
  • Manual: have peer @mention me, react to my message, reply to my thread - verify all three kinds appear, tabs filter, deep-links navigate.

Out of scope

  • Per-item read state + sidebar badge + mark-all-as-read (issue's "click-to-read" / "Mark all as read" criteria). Need a new activity_read_marks (user_id, kind, source_id, read_at) table; intentionally deferred to keep this PR focused on the read path.
  • Realtime push via the hub.
  • Bot-author filter and retention TTL (LC-82 open questions; no defaults chosen yet).
## Summary LC-82: `GET /activity` lists every event affecting the viewer across rooms - mentions of them, replies to threads they authored, reactions on messages they authored. On-demand UNION over the existing `mentions`, `messages`, and `message_reactions` tables; no new tables per the issue's "ship the cheap write path first" recommendation. ## Routes / UX - `GET /activity` - All tab. - `GET /activity?tab=mention|reply|reaction` - filtered. - Tabs in header; sidebar nav gains an "Activity" link beside Inbox / Saved. - Each row deep-links to `/{room|dm}/{id}#msg-{message_id}`. - Empty state: "Nothing to see here. New mentions, replies, and reactions will show up as they happen." ## Query shape Three SELECTs (one per kind), merged in Rust by `created_at DESC`, capped at 60 rows total. Each scopes to rooms the viewer can currently see (`r.room_type = 'public' OR EXISTS room_members rm WHERE rm.user_id = ?`) so messages from rooms the viewer has since left are hidden (LC-82 AC #6). Self-authored events suppressed (`m.user_id != ?` / `mr.user_id != ?`). ## Acceptance criteria - [x] `/activity` lists mentions, replies, reactions in time order across rooms the user can see. - [x] Tabs filter by kind. - [x] Clicking a row navigates to the source message (deep-link with `#msg-N`). Mark-read in source room is the existing flow on room open. - [ ] Sidebar unread badge - deferred (needs per-item read state). - [ ] Mark-all-as-read - deferred (needs per-item read state). - [x] Activity from rooms the user has left is hidden (access check in WHERE clause). ## Stack Independent of the unmerged stack (`feat/lc-81-jump-and-inbox` etc are now merged). Branches off current `main`. ## Test plan - [x] `just check`. - [x] `./dev/cargo test -p lets-chat-server --test routes_activity` - 2/2 pass (empty state, peer mention appears). - [ ] Manual: have peer @mention me, react to my message, reply to my thread - verify all three kinds appear, tabs filter, deep-links navigate. ## Out of scope - Per-item read state + sidebar badge + mark-all-as-read (issue's "click-to-read" / "Mark all as read" criteria). Need a new `activity_read_marks (user_id, kind, source_id, read_at)` table; intentionally deferred to keep this PR focused on the read path. - Realtime push via the hub. - Bot-author filter and retention TTL (LC-82 open questions; no defaults chosen yet).
feat(activity): per-user activity feed at /activity (LC-82)
Some checks failed
Check / clippy + fmt + tests (pull_request) Failing after 9s
b883ca43df
On-demand UNION over the existing `mentions`, `messages` (for
parent->reply edge), and `message_reactions` tables. No new tables;
ships the cheap-write path per the issue's recommendation. Per-item
read state and unread badge are follow-ups.

`GET /activity[?tab=all|mention|reply|reaction]` returns a server-
rendered list of activity rows. Each row:

- Mention: someone @-mentioned the viewer in a room they can see.
- Reply: someone posted a reply to a thread the viewer authored.
- Reaction: someone reacted to a message the viewer authored.

All three filter to rooms the viewer can currently see (public or
member-of), so messages from rooms the viewer has since left are
hidden. Author-of-self events suppressed (viewer reacting to their
own message isn't activity). Rows time-DESC, capped at 60 across all
kinds. Each row links to `/{room|dm}/{id}#msg-{message_id}`.

Tabs UI at top of page; sidebar nav gains an "Activity" link beside
Inbox / Saved.

Out of scope (open questions / follow-ups):

- Per-item read state + unread badge.
- Mark-all-as-read per tab.
- Realtime push via the hub.
- Bot-author filter.
- Retention TTL.

Tests: empty-state renders the explanatory copy; peer-authored @viewer
mention in #general appears in the viewer's activity feed with the
expected deep-link.
nrupard deleted branch feat/lc-82-activity 2026-05-18 20:16:15 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!142
No description provided.