diff --git a/client/src/handlers/room_join_request_handler.rs b/client/src/handlers/room_join_request_handler.rs index c5acccf..6a53825 100644 --- a/client/src/handlers/room_join_request_handler.rs +++ b/client/src/handlers/room_join_request_handler.rs @@ -3,11 +3,42 @@ use solstice_proto::server::RoomJoinRequest; use solstice_proto::ServerRequest; use crate::context::Context; +use crate::control; use crate::message_handler::MessageHandler; +use crate::room::RoomError; #[derive(Debug, Default)] pub struct RoomJoinRequestHandler; +fn start_joining(context: &Context, room_name: &str) -> Result<(), RoomError> { + let mut guard = context.state.lock(); + let result = guard.rooms.start_joining(room_name); + + if let Err(RoomError::MembershipChangeInvalid(_, _)) = result { + // This `expect()` should never fail since the error was not + // `RoomNotFound` but `MembershipChangeInvalid`. + let room = guard + .rooms + .get_strict(room_name) + .expect("querying room state"); + + let response = + control::Response::RoomJoinResponse(control::RoomJoinResponse { + room_name: room_name.to_string(), + room: room.clone(), + }); + + if let Err(err) = context.control_response_tx.blocking_send(response) { + error!( + "Failed to send RoomJoinResponse for room {}: {}", + room_name, err + ); + } + } + + result +} + impl MessageHandler for RoomJoinRequestHandler { type Message = String; @@ -16,14 +47,7 @@ impl MessageHandler for RoomJoinRequestHandler { context: &Context, room_name: &Self::Message, ) -> anyhow::Result<()> { - { - // TODO: Send response immediately if we already joined. - let mut guard = context.state.lock(); - guard - .rooms - .start_joining(room_name) - .context("joining room")?; - } + start_joining(context, room_name).context("joining room")?; context .server_request_tx .blocking_send(ServerRequest::RoomJoinRequest(RoomJoinRequest { @@ -41,12 +65,13 @@ impl MessageHandler for RoomJoinRequestHandler { #[cfg(test)] mod tests { use anyhow::Context; - use solstice_proto::server::{RoomJoinRequest, RoomListResponse}; + use solstice_proto::server::RoomJoinRequest; use solstice_proto::ServerRequest; - use crate::context::ContextBundle; + use crate::context::{ContextBundle, ContextOptions}; + use crate::control; use crate::message_handler::MessageHandler; - use crate::room::Membership; + use crate::room::{Membership, Room, Visibility}; use super::RoomJoinRequestHandler; @@ -69,20 +94,48 @@ mod tests { } #[test] - fn run_success() -> anyhow::Result<()> { - let mut bundle = ContextBundle::default(); + fn run_already_joined_responds_immediately() -> anyhow::Result<()> { + let mut room = Room::new(Visibility::Public, 3); + room.membership = Membership::Member; - bundle - .context - .state - .lock() + let mut options = ContextOptions::default(); + options + .initial_state .rooms - .set_room_list(RoomListResponse { - rooms: vec![("bleep".to_string(), 3)], - owned_private_rooms: vec![], - other_private_rooms: vec![], - operated_private_room_names: vec![], - }); + .insert("bleep".to_string(), room.clone()); + let mut bundle = ContextBundle::new(options); + + RoomJoinRequestHandler::default() + .run(&bundle.context, &"bleep".to_string()) + .unwrap_err(); + + // Room state has not changed. + assert_eq!( + bundle.context.state.lock().rooms.get_room_list(), + vec![("bleep".to_string(), room.clone())] + ); + + assert_eq!( + bundle.control_response_rx.blocking_recv(), + Some(control::Response::RoomJoinResponse( + control::RoomJoinResponse { + room_name: "bleep".to_string(), + room, + } + )) + ); + + Ok(()) + } + + #[test] + fn run_success() -> anyhow::Result<()> { + let mut options = ContextOptions::default(); + options + .initial_state + .rooms + .insert("bleep".to_string(), Room::new(Visibility::Public, 3)); + let mut bundle = ContextBundle::new(options); RoomJoinRequestHandler::default() .run(&bundle.context, &"bleep".to_string())