use dioxus::prelude::*;
use matrix_sdk::ruma::OwnedRoomId;
use crate::state::app_state::{AppState, AppView};
#[component]
pub fn JoinRoomDialog(on_close: EventHandler<()>) -> Element {
let mut state = use_context::<Signal<AppState>>();
let mut room_address = use_signal(|| String::new());
let mut is_joining = use_signal(|| false);
let mut join_error = use_signal(|| Option::<String>::None);
let on_join = move |_| {
let address = room_address.read().trim().to_string();
if address.is_empty() {
join_error.set(Some("Please enter a room ID or alias".to_string()));
return;
}
is_joining.set(true);
join_error.set(None);
spawn(async move {
match join_room(state, &address).await {
Ok(room_id) => {
tracing::info!("Joined room: {room_id}");
let mut s = state.write();
s.active_room_id = Some(room_id.clone());
s.current_view = AppView::Room(room_id);
drop(s);
on_close.call(());
}
Err(e) => {
tracing::error!("Failed to join room: {e}");
join_error.set(Some(e));
}
}
is_joining.set(false);
});
};
let on_keydown = move |evt: Event<KeyboardData>| {
if evt.key() == Key::Enter {
evt.prevent_default();
let address = room_address.read().trim().to_string();
if address.is_empty() {
return;
}
is_joining.set(true);
join_error.set(None);
spawn(async move {
match join_room(state, &address).await {
Ok(room_id) => {
let mut s = state.write();
s.active_room_id = Some(room_id.clone());
s.current_view = AppView::Room(room_id);
drop(s);
on_close.call(());
}
Err(e) => {
join_error.set(Some(e));
}
}
is_joining.set(false);
});
}
};
rsx! {
div {
class: "modal-overlay",
onclick: move |_| on_close.call(()),
div {
class: "modal-dialog join-room-dialog",
onclick: move |evt| evt.stop_propagation(),
div {
class: "modal-dialog__header",
h2 { "Join Room" }
button {
class: "modal-dialog__close",
onclick: move |_| on_close.call(()),
"✕"
}
}
div {
class: "modal-dialog__body",
if let Some(err) = join_error.read().as_ref() {
div {
class: "join-room-dialog__error",
"{err}"
}
}
div {
class: "join-room-dialog__field",
label { "Room ID or Alias" }
input {
r#type: "text",
placeholder: "e.g. !roomid:server.org or #alias:server.org",
value: "{room_address}",
oninput: move |evt| room_address.set(evt.value()),
onkeydown: on_keydown,
disabled: *is_joining.read(),
autofocus: true,
}
}
p {
class: "join-room-dialog__hint",
"Enter a room ID (starting with !) or a room alias (starting with #)"
}
}
div {
class: "modal-dialog__footer",
button {
class: "btn btn--secondary",
onclick: move |_| on_close.call(()),
disabled: *is_joining.read(),
"Cancel"
}
button {
class: "btn btn--primary",
onclick: on_join,
disabled: room_address.read().trim().is_empty() || *is_joining.read(),
if *is_joining.read() {
"Joining..."
} else {
"Join"
}
}
}
}
}
}
}
async fn join_room(
state: Signal<AppState>,
address: &str,
) -> Result<OwnedRoomId, String> {
let client = { state.read().client.clone() };
let client = client.ok_or_else(|| "Not logged in".to_string())?;
let room = if address.starts_with('!') {
let room_id: OwnedRoomId = address
.try_into()
.map_err(|e| format!("Invalid room ID: {e}"))?;
client
.join_room_by_id(&room_id)
.await
.map_err(|e| format!("Failed to join room: {e}"))?
} else if address.starts_with('#') {
use matrix_sdk::ruma::OwnedRoomOrAliasId;
let alias: OwnedRoomOrAliasId = address
.try_into()
.map_err(|e| format!("Invalid room alias: {e}"))?;
client
.join_room_by_id_or_alias(alias.as_ref(), &[])
.await
.map_err(|e| format!("Failed to join room: {e}"))?
} else {
return Err("Address must start with ! (room ID) or # (room alias)".to_string());
};
Ok(room.room_id().to_owned())
}