fix(sidebar): move add-room form to a modal so live swaps can't clobber it (LC-400) #416

Merged
longjacksonle merged 1 commit from fix/lc-400-addroom-modal into main 2026-06-20 23:35:53 +02:00

Bug (LC-400)

Inside an enclave, creating a SECOND room was impossible: clicking the ROOMS-header + opened the add-room popover, but as soon as you typed the name the popover vanished, so the form could never be submitted. The first room worked because its popover was submitted before any live update landed.

Cause

After LC-336 the create-room form was an inline <details> popover rendered inside #sidebar-nav-{enclave} - a live-updating region. Creating a room broadcasts ChatEvent::EnclaveRoomAdded; the user's own tab is subscribed to the enclave topic and handles it by OOB-swapping the entire #sidebar-nav (render_enclave_sidebar_nav -> SidebarNavLiveFragment). That swap rebuilds the nav DOM, destroying the open popover and the half-typed input. Any enclave event arriving during the typing window (the just-created room, another member, presence, a second tab) wiped the form.

This is the LC-179 class of bug: a full-region live swap clobbers client-only, in-progress UI state the server can't see.

Fix (option 2 from the ticket: move it out of the live region)

  • New add_room_modal.html: a singleton modal included in the persistent shell (layout.html), OUTSIDE <aside id="sidebar">, so no sidebar OOB swap can touch it. Mirrors the existing poll/scheduled modal pattern (.modal-backdrop / .modal-panel / __lcDialogTrap, Escape + backdrop-click close, focus restore).
  • The sidebar + is now a plain <button> that opens the modal via window.__lcOpenAddRoom(). It carries no form state, so it is safe to re-render on every swap.
  • The form remains a native POST /enclave/{id}/rooms, so post_create_room's existing behavior - redirect into the newly created room, duplicate-name flash - is unchanged on full-page submit.
  • Gated identically to the old popover (can_manage_sidebar_categories + an enclave context).

Scope

Frontend only: add_room_modal.html (new), layout.html (include), partials/sidebar_nav.html (popover -> button). No Rust, route, or handler change. The room-create wiring (fields, route, RBAC) was already correct; this only stops the live swap from destroying the open form.

Tests

just check (standalone + saas compile, clippy, fmt), just test, and just test-saas all pass. just build-css regenerated. No test asserted on the old popover markup (tests POST to the route directly).

## Bug (LC-400) Inside an enclave, creating a SECOND room was impossible: clicking the ROOMS-header `+` opened the add-room popover, but as soon as you typed the name the popover vanished, so the form could never be submitted. The first room worked because its popover was submitted before any live update landed. ## Cause After LC-336 the create-room form was an inline `<details>` popover rendered inside `#sidebar-nav-{enclave}` - a live-updating region. Creating a room broadcasts `ChatEvent::EnclaveRoomAdded`; the user's own tab is subscribed to the enclave topic and handles it by OOB-swapping the entire `#sidebar-nav` (`render_enclave_sidebar_nav` -> `SidebarNavLiveFragment`). That swap rebuilds the nav DOM, destroying the open popover and the half-typed input. Any enclave event arriving during the typing window (the just-created room, another member, presence, a second tab) wiped the form. This is the LC-179 class of bug: a full-region live swap clobbers client-only, in-progress UI state the server can't see. ## Fix (option 2 from the ticket: move it out of the live region) - New `add_room_modal.html`: a singleton modal included in the persistent shell (`layout.html`), OUTSIDE `<aside id="sidebar">`, so no sidebar OOB swap can touch it. Mirrors the existing poll/scheduled modal pattern (`.modal-backdrop` / `.modal-panel` / `__lcDialogTrap`, Escape + backdrop-click close, focus restore). - The sidebar `+` is now a plain `<button>` that opens the modal via `window.__lcOpenAddRoom()`. It carries no form state, so it is safe to re-render on every swap. - The form remains a native `POST /enclave/{id}/rooms`, so `post_create_room`'s existing behavior - redirect into the newly created room, duplicate-name flash - is unchanged on full-page submit. - Gated identically to the old popover (`can_manage_sidebar_categories` + an enclave context). ## Scope Frontend only: `add_room_modal.html` (new), `layout.html` (include), `partials/sidebar_nav.html` (popover -> button). No Rust, route, or handler change. The room-create wiring (fields, route, RBAC) was already correct; this only stops the live swap from destroying the open form. ## Tests `just check` (standalone + saas compile, clippy, fmt), `just test`, and `just test-saas` all pass. `just build-css` regenerated. No test asserted on the old popover markup (tests POST to the route directly).
fix(sidebar): move add-room form to a modal so live swaps can't clobber it (LC-400)
All checks were successful
check-secrets / TruffleHog (push) Successful in 4s
check-secrets / Nosey parker (push) Successful in 4s
check-secrets / Kingfisher (push) Successful in 5s
check-secrets / Nosey parker (pull_request) Successful in 3s
check-secrets / Kingfisher (pull_request) Successful in 5s
check-secrets / TruffleHog (pull_request) Successful in 6s
Create release / Create release from merged PR (pull_request) Has been skipped
Check / clippy + fmt + tests (pull_request) Successful in 3m3s
8ef08c7584
After LC-336 the "Add room" form was an inline <details> popover inside the sidebar ROOMS header, which lives in the live-updating #sidebar-nav region. Creating a room broadcasts EnclaveRoomAdded; the user's own tab receives it and OOB-swaps the entire #sidebar-nav (render_enclave_sidebar_nav). That swap destroyed the open popover and the half-typed name mid-typing, so a second room could never be submitted (the form vanished before submit). The room the user did see created was the first one, submitted before any swap landed.

Move the create-room form into a singleton modal (add_room_modal.html) included in the persistent shell (layout.html), OUTSIDE #sidebar, so no sidebar OOB swap can touch it. The sidebar `+` is now a plain button that opens the modal via window.__lcOpenAddRoom(); it is safe to re-render on every swap because it only triggers the opener. Mirrors the existing poll/scheduled modal pattern (.modal-backdrop / .modal-panel / __lcDialogTrap, Escape + backdrop-click + focus-restore).

The form stays a native POST to /enclave/{id}/rooms, so post_create_room's existing behavior (redirect into the created room, duplicate-name flash) is unchanged on full-page submit. Gated identically to the old popover (enclave context + manage rights).

Frontend only (templates), no Rust change. just check, just test, just test-saas all pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle deleted branch fix/lc-400-addroom-modal 2026-06-20 23:35: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!416
No description provided.