diff --git a/server/src/html/forum.rs b/server/src/html/forum.rs index f6e45b05bb92126966e304619f80ef1d45be7a4b..a2d36c0645cd5cdd97da78151a2fd574175e67f8 100644 --- a/server/src/html/forum.rs +++ b/server/src/html/forum.rs @@ -965,18 +965,23 @@ pub async fn room_page( drop(reduced); return (StatusCode::NOT_FOUND, "room not found").into_response(); }; - let slug_display = room_slug.as_str(); + // Precompute markup that needs `&ReducerState`, then release the lock before `layout`. + // `tokio::sync::RwLock` queues new readers behind a waiting writer; holding the read + // guard across HTML/stringify could stall other navigations when an ingest needs a write lock. + let members_markup = room_members_section_markup(&reduced, &room_id, false); let forum_cli = format!("npx slugsocial private {room_id} forum list"); let garden_cli = format!("npx slugsocial private {room_id} garden tree"); let audit_cli = format!("npx slugsocial private {room_id} audit"); + drop(reduced); + let slug_display = room_slug.as_str(); let page = layout( &format!("room {slug_display} — slug.social"), "view-thread", html! { (strip) nav class="breadcrumb" { (bc_room(&nav, slug_display, None)) } - (room_members_section_markup(&reduced, &room_id, false)) + (members_markup) h3 { "threads" } (render_thread_feed(Some(&nav), "room-thread-feed", &rows, now)) @if show_new { @@ -992,7 +997,6 @@ pub async fn room_page( theme_from_jar(&jar), &theme_next_from_uri(&uri), ); - drop(reduced); Html(page.into_string()).into_response() }