Problem
ui/src/components/room_list/edit_room_modal.rs closes the room-details panel
by writing a GlobalSignal directly inside an onclick, with no
crate::util::defer():
edit_room_modal.rs:66 — the backdrop overlay
edit_room_modal.rs:375 — the ✕ close button
onclick: move |_| {
EDIT_ROOM_MODAL.write().room = None;
},
.claude/rules/dioxus-signal-safety.md names this shape explicitly as wrong:
ALSO WRONG — onclick handlers trigger the same RefCell panic
onclick: move |_| {
ROOMS.write().map.remove(&key);
};
The rationale is that a signal write's Drop fires subscriber notifications
synchronously, and Firefox mobile runs those during Drop, so a memo doing
try_read() on the same signal can hit a re-entrant RefCell already borrowed
panic. defer() also supplies the Dioxus runtime + root scope.
The leave-room path in the same file (line 333) already does this correctly,
inside a crate::util::defer() block — so this is an inconsistency within one
component, not a deliberate exemption.
Evidence / severity
No symptom reproduced. An instrumented 25-cycle open/copy/close/reopen loop
against both WebKit and Firefox (alternating close-button and backdrop routes)
produced no panic and no stale render. So this is filed as a latent
rule violation, not a known-live bug — severity low, correctness risk real
but unobserved.
Deliberately not fixed in #536 (the Firefox selection fix): making the close
deferred changes modal-close timing app-wide, which is a bigger blast radius
than that PR warrants without evidence of harm. Noting it here so it is not
silently lost.
Suggested fix
Wrap both writes:
onclick: move |_| {
crate::util::defer(move || {
EDIT_ROOM_MODAL.write().room = None;
});
},
Then confirm copy-clipboard-feedback.spec.ts (which closes this class of
modal via the backdrop) and the room-details specs still pass — the close
becomes asynchronous, so any assertion that races it would surface there.
Worth grepping for the same shape in the other modals while in there.
[AI-assisted - Claude]
Problem
ui/src/components/room_list/edit_room_modal.rscloses the room-details panelby writing a GlobalSignal directly inside an
onclick, with nocrate::util::defer():edit_room_modal.rs:66— the backdrop overlayedit_room_modal.rs:375— the ✕ close button.claude/rules/dioxus-signal-safety.mdnames this shape explicitly as wrong:The rationale is that a signal write's Drop fires subscriber notifications
synchronously, and Firefox mobile runs those during Drop, so a memo doing
try_read()on the same signal can hit a re-entrantRefCell already borrowedpanic.
defer()also supplies the Dioxus runtime + root scope.The leave-room path in the same file (line 333) already does this correctly,
inside a
crate::util::defer()block — so this is an inconsistency within onecomponent, not a deliberate exemption.
Evidence / severity
No symptom reproduced. An instrumented 25-cycle open/copy/close/reopen loop
against both WebKit and Firefox (alternating close-button and backdrop routes)
produced no panic and no stale render. So this is filed as a latent
rule violation, not a known-live bug — severity low, correctness risk real
but unobserved.
Deliberately not fixed in #536 (the Firefox selection fix): making the close
deferred changes modal-close timing app-wide, which is a bigger blast radius
than that PR warrants without evidence of harm. Noting it here so it is not
silently lost.
Suggested fix
Wrap both writes:
Then confirm
copy-clipboard-feedback.spec.ts(which closes this class ofmodal via the backdrop) and the room-details specs still pass — the close
becomes asynchronous, so any assertion that races it would surface there.
Worth grepping for the same shape in the other modals while in there.
[AI-assisted - Claude]