fix/axum-rewrite-gaps #26

Merged
nrupard merged 4 commits from fix/axum-rewrite-gaps into main 2026-05-04 15:46:48 +02:00
Owner
No description provided.
Bring back four UX features that existed on origin/long-lc-24 but did not survive the Dioxus to Askama+HTMX rewrite, plus a delete-permission bug.

- Clickable message author username links to /dm/{user_id} so users can start a DM by clicking any author who is not themselves. MessageView now carries user_id and viewer_id; message.html switches between an anchor and a span based on whether the viewer is the author.
- Sidebar footer gains a "Settings" link for every user and an "Admin" or "Moderate" link for users whose role is admin or moderator. Without these the /admin/* pages and per-user preferences were unreachable from the UI.
- New /settings page (GET and POST) lets users toggle send and receive read receipts. The DB setter db::auth::set_read_receipts_enabled already existed; the page was the missing piece.
- Mods and admins can now delete messages they did not author. The delete handler already allowed it, but the message template hid the Delete button behind message.can_edit (author-only). Split into can_edit and can_delete and gate each button independently. A standalone React button is also surfaced on hover so reactions are discoverable without first hovering the existing reaction bar.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
After the previous commit, two follow-up reports surfaced. (1) Edit, Delete, and the React picker did not appear unless the user first navigated to the new /settings or /admin/settings page. (2) Clicking an author's username did not open a DM. Both turned out to be variations of the same root cause: stale browser-cached CSS and an unobtrusive UI.

The original message template hid the action buttons behind opacity-0 plus group-hover:opacity-100. That works only when the cached stylesheet contains the group-hover:opacity-100 utility. The asset_version cache buster was the static crate version (0.1.0), so browsers that had cached the pre-rewrite tailwind-built.css kept using it indefinitely and the new utilities never reached the page. Visiting one of the brand-new admin or settings URLs happened to trigger a fresh stylesheet load on some browsers, which is why the buttons appeared after that detour.

Two changes address it:

- Cache buster derived from the mtime of server/assets/tailwind-built.css. Every just build-css produces a new query string so browsers pick up the new stylesheet immediately. Falls back to the current Unix timestamp if the file cannot be stat'd, so cache busting is never silently disabled. asset_version becomes an owned String on AppState, so all template structs now borrow it as &state.asset_version.
- Action buttons (React, Edit, Delete) are always visible at the right edge of each message instead of fading in on hover. The username also gains text-blue-700 so the link is recognisable as a link rather than plain text. These are robust against stale CSS and avoid relying on hover affordances for primary actions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Live updates were broken in three ways after the rewrite:

1. MessageEdited replaced the whole #msg-{id} element with a stub fragment that lost author, timestamp, reactions, and the edit/delete actions. Editing a message visually destroyed its surroundings until refresh.
2. NewMessage rendered a minimal fragment without the DM link, edit/delete controls, or reactions container, so freshly broadcast messages looked second-class until refresh.
3. RoomMemberAdded and RoomMemberRemoved were wired through the hub but had no fragment renderers and no callers, so a brand-new DM never appeared in the recipient's sidebar without a refresh.

Fixes:

- room/message.html now accepts an `oob` flag (defaulting to false in messages.html, edit forms, and the standard single-message fragment). When true, the rendered <div id="msg-{id}"> carries hx-swap-oob="outerHTML", so the same template doubles as a WS edit fragment without duplicating markup.
- ws/new_message.html and ws/edited_message.html now include room/message.html instead of hand-rolled minimal markup. The WS sender task builds a per-recipient MessageView with viewer_id and recomputed can_edit / can_delete, so each tab sees actions matching its own permissions.
- partials/sidebar.html gains id="sidebar" and conditional hx-swap-oob="outerHTML" via the same `oob` pattern. ws/sidebar_update.html includes it with oob=true. The WS handler renders this for RoomMemberAdded / RoomMemberRemoved when the event targets the current viewer, recomputing the unread map and DM peer list as a side effect.
- routes/dm.rs broadcasts RoomMemberAdded to both participants on first DM creation via state.hub.broadcast_to_user, so the recipient's sidebar picks up the new DM live.

render_event now only handles events that are recipient-independent (MessageDeleted, UserTyping, UserStoppedTyping). Per-recipient events (NewMessage, MessageEdited, ReactionAdded, ReactionRemoved, DmRead, RoomMemberAdded, RoomMemberRemoved) are rendered in the WS sender task where the recipient identity is in scope.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A sweep of the admin pages found that several features that existed on the pre-rewrite branch (origin/long-lc-24) were never ported. The HTTP routes and DB access functions for them mostly already existed; only the wiring and the admin UI were missing. This commit fills in the gaps and writes mod_actions entries for the privileged actions so the moderation log captures them.

Users page (/admin/users):
- Inline role selector (user / moderator / admin) per row, posting to /admin/users/{id}/role.
- Mute / Unmute buttons posting to /admin/users/{id}/mute and /admin/users/{id}/unmute. Status column now shows both Banned and Muted independently.
- Delete button posting to /admin/users/{id}/delete. Refuses to delete the acting admin. Also deletes the target's sessions before the user row to avoid stale session lookups.

Rooms page (/admin/rooms):
- Create-room form at the top of the page. Public rooms have no invite code; private rooms get a random invite code at creation time.
- Per-row collapsible details with an Edit form (name + topic) and, for private rooms, an Invite-by-username form and a Regenerate-invite-link button.
- Invite column shows /invite/{code} for private rooms and a dash for public rooms.
- Archive renamed to Delete in the UI; same handler.

Each privileged action (ban, unban, mute, unmute, role change, user delete, room delete, room invite) writes a mod_actions row via db::moderation::log_mod_action so /admin/modlog has a record.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
nrupard deleted branch fix/axum-rewrite-gaps 2026-05-04 15:46:48 +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!26
No description provided.