Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -311,12 +311,20 @@ jobs:
run: npx playwright test
working-directory: ./ui/tests

# `always()`, not `failure()`: with `retries: 2` an intermittent test is
# reported as "flaky" and the job still succeeds, so a `failure()` guard
# threw away the one artifact that could explain it (see freenet/river#538,
# where the trace was gone and the attached hypothesis turned out to be
# wrong). The playwright config keeps a trace `on-first-retry`, so this
# uploads the flake evidence too. On a clean run the directory is empty and
# this is a no-op.
- name: Upload test results
if: failure()
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report
path: ui/tests/test-results/
if-no-files-found: ignore

claude-ci-analysis:
name: Claude CI Analysis
Expand Down
7 changes: 6 additions & 1 deletion ui/src/components/room_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -389,8 +389,13 @@ pub fn RoomList() -> Element {
"data-testid": "create-room-button",
class: "p-1.5 rounded-md text-text-muted hover:text-accent hover:bg-surface transition-colors",
title: "Create Room",
// Signal mutation from an event handler must be deferred
// (dioxus-signal-safety: direct writes here are the
// Firefox mobile RefCell re-entrancy crash path).
onclick: move |_| {
CREATE_ROOM_MODAL.write().show = true;
crate::util::defer(move || {
CREATE_ROOM_MODAL.write().show = true;
});
},
Icon { width: 14, height: 14, icon: FaPlus }
}
Expand Down
17 changes: 13 additions & 4 deletions ui/src/components/room_list/create_room_modal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,14 @@ pub fn CreateRoomModal() -> Element {
// Backdrop
div {
class: "fixed inset-0 bg-black/50 z-40",
// Signal mutation from an event handler must be deferred
// (dioxus-signal-safety: direct writes here are the Firefox
// mobile RefCell re-entrancy crash path).
onclick: move |_| {
CREATE_ROOM_MODAL.with_mut(|modal| {
modal.show = false;
crate::util::defer(move || {
CREATE_ROOM_MODAL.with_mut(|modal| {
modal.show = false;
});
});
}
}
Expand Down Expand Up @@ -211,9 +216,13 @@ pub fn CreateRoomModal() -> Element {
button {
"data-testid": "create-room-cancel-button",
class: "px-4 py-2 text-sm text-text-muted hover:text-text hover:bg-surface rounded-lg transition-colors",
// Deferred close — same signal-safety rule as the
// backdrop handler above.
onclick: move |_| {
CREATE_ROOM_MODAL.with_mut(|modal| {
modal.show = false;
crate::util::defer(move || {
CREATE_ROOM_MODAL.with_mut(|modal| {
modal.show = false;
});
});
},
"Cancel"
Expand Down
13 changes: 11 additions & 2 deletions ui/src/components/room_list/edit_room_modal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,13 @@ pub fn EditRoomModal() -> Element {
// Overlay
div {
class: "absolute inset-0 bg-black/50",
// Signal mutation from an event handler must be deferred
// (dioxus-signal-safety: direct writes here are the Firefox
// mobile RefCell re-entrancy crash path).
onclick: move |_| {
EDIT_ROOM_MODAL.write().room = None;
crate::util::defer(move || {
EDIT_ROOM_MODAL.write().room = None;
});
}
}
// Modal content
Expand Down Expand Up @@ -371,8 +376,12 @@ pub fn EditRoomModal() -> Element {
button {
"data-testid": "edit-room-close-button",
class: "absolute top-3 right-3 p-1 text-text-muted hover:text-text transition-colors",
// Deferred close — same signal-safety rule as the
// backdrop handler above.
onclick: move |_| {
EDIT_ROOM_MODAL.write().room = None;
crate::util::defer(move || {
EDIT_ROOM_MODAL.write().room = None;
});
},
"✕"
}
Expand Down
30 changes: 23 additions & 7 deletions ui/src/components/room_list/receive_invitation_modal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -592,21 +592,37 @@ fn render_error_state(
let _ = element.set_focus(true).await;
});
},
// Signal mutation from an event handler must be deferred
// (dioxus-signal-safety: direct writes here are the Firefox
// mobile RefCell re-entrancy crash path).
onclick: move |_| {
// Reset to PendingSubscription so the synchronizer retries
PENDING_INVITES.with_mut(|pending| {
if let Some(join) = pending.map.get_mut(&room_key) {
join.status = PendingRoomStatus::PendingSubscription;
}
crate::util::defer(move || {
// Reset to PendingSubscription so the synchronizer retries
PENDING_INVITES.with_mut(|pending| {
if let Some(join) = pending.map.get_mut(&room_key) {
join.status = PendingRoomStatus::PendingSubscription;
}
});
});
},
"Retry"
}
button {
class: "px-4 py-2 bg-surface hover:bg-surface-hover text-text rounded-lg transition-colors",
// Deferred — same signal-safety rule as the Retry handler
// above. BOTH statements go inside the defer so their
// relative order is preserved (code after a `defer()` runs
// BEFORE the deferred closure), and because
// `dismiss_invitation_persistently` itself writes a signal
// via `invitation.set(None)`. The clone is needed because
// an `onclick` is `FnMut`, so the non-`Copy` invitation
// cannot be moved out of the captured environment.
onclick: move |_| {
PENDING_INVITES.write().map.remove(&room_key);
dismiss_invitation_persistently(&inv_for_dismiss, invitation);
let inv = inv_for_dismiss.clone();
crate::util::defer(move || {
PENDING_INVITES.write().map.remove(&room_key);
dismiss_invitation_persistently(&inv, invitation);
});
},
"Dismiss"
}
Expand Down
Loading
Loading