feat(sidebar): per-user starred rooms + DMs (LC-80) [stacked] #139

Merged
nrupard merged 1 commit from feat/lc-80-starred-rooms into main 2026-05-18 19:25:53 +02:00
Owner

Summary

LC-80: per-user starring / favoriting of rooms and DMs. Starred items render in a dedicated "Starred" section at the top of the sidebar, ordered by a user-controlled position. Drag-and-drop reorder works inside the section.

Stacked PR

This branch is on top of:

  • refactor/lc-79-enclave-scoped-categories (PR #137)
  • fix/sidebar-active-room-and-uncategorize (PR #138)

Per the user's "do it all on one branch" instruction. Merge this branch supersedes #137 and #138; consider closing those in favor of this single merge.

Schema

auth/0018_starred_rooms.sql:

starred_rooms (user_id TEXT, room_id INTEGER, position INTEGER,
               starred_at TEXT, PK(user_id, room_id))

room_id references a chat.db rooms row; cross-db FK not enforced, so forget_room/forget_rooms are called on leave/kick.

Routes

  • POST /rooms/{room_id}/star toggles. Gated by is_room_accessible to keep private-room existence non-disclosable.
  • PATCH /sidebar/stars/positions accepts comma-separated ids and rewrites per-row positions for the calling user.

Sidebar plumbing

  • SidebarPeer gains dm_room_id so DM stars key on the same chat.db room id as room stars.
  • load_sidebar now pulls the starred set + positions, splits sidebar_rooms and sidebar_peers into starred/non-starred, and pulls starred rooms back OUT of their categories so a starred categorized room appears only in Starred (not duplicated). Wider return tuple; view structs gain sidebar_starred_rooms and sidebar_starred_peers via batch regex update (~22 files).
  • New partials/sidebar_peer_row.html mirrors the room row so the Starred section can mix both types in one list.
  • Star toggle button on each row: filled star when starred, hollow + reveal-on-hover when not.

Leave / kick

forget_rooms runs on post_leave / post_kick; forget_room runs on post_remove_room_member. AC #4 covered.

Acceptance criteria

  • Starring a room moves it to the Starred section.
  • Per-user, persists across devices (auth.db).
  • DnD reorder within Starred (PATCH /sidebar/stars/positions).
  • Leaving a room unstars it (forget_room / forget_rooms on leave / kick / room-removal).
  • DMs can be starred too, same section (SidebarPeer::dm_room_id keys on the DM's room_id).

Test plan

  • just check (fmt + clippy across standalone + saas).
  • ./dev/cargo test -p lets-chat-server --test routes_starred_rooms - 3/3 pass.
  • Manual: star a room - verify it jumps to top in Starred section, the star turns filled. Star a DM. Drag-reorder. Unstar.
## Summary LC-80: per-user starring / favoriting of rooms and DMs. Starred items render in a dedicated "Starred" section at the top of the sidebar, ordered by a user-controlled position. Drag-and-drop reorder works inside the section. ## Stacked PR This branch is on top of: - `refactor/lc-79-enclave-scoped-categories` (PR #137) - `fix/sidebar-active-room-and-uncategorize` (PR #138) Per the user's "do it all on one branch" instruction. Merge this branch supersedes #137 and #138; consider closing those in favor of this single merge. ## Schema `auth/0018_starred_rooms.sql`: ``` starred_rooms (user_id TEXT, room_id INTEGER, position INTEGER, starred_at TEXT, PK(user_id, room_id)) ``` `room_id` references a chat.db rooms row; cross-db FK not enforced, so `forget_room`/`forget_rooms` are called on leave/kick. ## Routes - `POST /rooms/{room_id}/star` toggles. Gated by `is_room_accessible` to keep private-room existence non-disclosable. - `PATCH /sidebar/stars/positions` accepts comma-separated `ids` and rewrites per-row positions for the calling user. ## Sidebar plumbing - `SidebarPeer` gains `dm_room_id` so DM stars key on the same chat.db room id as room stars. - `load_sidebar` now pulls the starred set + positions, splits `sidebar_rooms` and `sidebar_peers` into starred/non-starred, and pulls starred rooms back OUT of their categories so a starred categorized room appears only in Starred (not duplicated). Wider return tuple; view structs gain `sidebar_starred_rooms` and `sidebar_starred_peers` via batch regex update (~22 files). - New `partials/sidebar_peer_row.html` mirrors the room row so the Starred section can mix both types in one list. - Star toggle button on each row: filled star when starred, hollow + reveal-on-hover when not. ## Leave / kick `forget_rooms` runs on `post_leave` / `post_kick`; `forget_room` runs on `post_remove_room_member`. AC #4 covered. ## Acceptance criteria - [x] Starring a room moves it to the Starred section. - [x] Per-user, persists across devices (auth.db). - [x] DnD reorder within Starred (`PATCH /sidebar/stars/positions`). - [x] Leaving a room unstars it (`forget_room` / `forget_rooms` on leave / kick / room-removal). - [x] DMs can be starred too, same section (`SidebarPeer::dm_room_id` keys on the DM's room_id). ## Test plan - [x] `just check` (fmt + clippy across standalone + saas). - [x] `./dev/cargo test -p lets-chat-server --test routes_starred_rooms` - 3/3 pass. - [ ] Manual: star a room - verify it jumps to top in Starred section, the star turns filled. Star a DM. Drag-reorder. Unstar.
feat(sidebar): per-user starred rooms + DMs (LC-80)
All checks were successful
Check / clippy + fmt + tests (pull_request) Successful in 58s
40c6ecad3c
Adds per-user starring of rooms and DMs. Starred items render in a
dedicated "Starred" section at the top of the sidebar, ordered by a
user-controlled position. Drag-and-drop reorder works inside the
section.

Schema:
- auth migration 0018 creates `starred_rooms (user_id, room_id, position, starred_at, PK(user_id, room_id))`.

DB:
- `db::starred_rooms` exposes star / unstar / is_starred / starred_room_ids / star_positions / set_positions / forget_room / forget_rooms.

Routes:
- `POST /rooms/{room_id}/star` toggles (admin or any accessible room). Membership-gated by `is_room_accessible` so private-room existence isn't disclosed.
- `PATCH /sidebar/stars/positions` accepts `ids=a,b,c` and rewrites positions per row.

Sidebar plumbing:
- `SidebarPeer` gains `dm_room_id` so DM stars key on the same chat.db room id as room stars.
- `load_sidebar` now pulls the user's starred set + positions, splits both `sidebar_rooms` and `sidebar_peers` into starred/non-starred, and prepends starred rooms back into the Starred bucket from inside any category they were assigned to (a starred room is shown ONCE - in Starred, not duplicated in its category). Returns a wider tuple; view structs gain `sidebar_starred_rooms` and `sidebar_starred_peers` (mechanical batch update across ~22 sites via regex).
- New `partials/sidebar_peer_row.html` mirrors `sidebar_room_row.html` so the Starred section can render rooms and DMs in one list.
- Each row template has a star toggle button (filled star when starred, hollow + reveal-on-hover otherwise).

Leave / kick:
- `db::starred_rooms::forget_rooms` runs in `post_leave` and `post_kick`; `forget_room` runs in `post_remove_room_member` (LC-80 AC #4).

Tests:
- New `routes_starred_rooms` with 3 tests: toggle inserts then removes, inaccessible-room is 403, positions endpoint reorders. All green.
- 18 hand-rolled test files updated with auth migration 0018.

Stacks on the LC-79 enclave-scoped refactor + the LC-79 follow-up
(active-room-highlight + drag-to-uncategorize); merging this branch
brings all three together.
nrupard deleted branch feat/lc-80-starred-rooms 2026-05-18 19:25:53 +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!139
No description provided.