use dioxus::prelude::*;
use crate::app::Route;
use crate::client;
use crate::state::app_state::{AppState, AuthStatus};
#[component]
pub fn RegisterForm() -> Element {
let mut state = use_context::<Signal<AppState>>();
let navigator = use_navigator();
let mut homeserver = use_signal(|| "https://matrix.org".to_string());
let mut username = use_signal(|| String::new());
let mut password = use_signal(|| String::new());
let mut confirm_password = use_signal(|| String::new());
let mut error_message = use_signal(|| Option::<String>::None);
let mut is_loading = use_signal(|| false);
let on_submit = move |evt: Event<FormData>| {
evt.prevent_default();
let hs = homeserver.read().clone();
let user = username.read().clone();
let pass = password.read().clone();
let confirm = confirm_password.read().clone();
if user.trim().is_empty() {
error_message.set(Some("Please enter a username".to_string()));
return;
}
if pass.len() < 8 {
error_message.set(Some("Password must be at least 8 characters".to_string()));
return;
}
if pass != confirm {
error_message.set(Some("Passwords do not match".to_string()));
return;
}
is_loading.set(true);
error_message.set(None);
spawn(async move {
match register_account(&hs, &user, &pass).await {
Ok(matrix_client) => {
tracing::info!("Registration successful");
{
let mut w = state.write();
w.auth_status = AuthStatus::LoggedIn;
w.client = Some(matrix_client);
}
navigator.push(Route::Home {});
}
Err(e) => {
tracing::error!("Registration failed: {e}");
error_message.set(Some(e));
is_loading.set(false);
}
}
});
};
rsx! {
form {
class: "register-form",
onsubmit: on_submit,
h2 { class: "register-form__title", "Create Account" }
if let Some(ref err) = *error_message.read() {
div {
class: "register-form__error",
"{err}"
}
}
div {
class: "register-form__field",
label { r#for: "homeserver", "Homeserver" }
input {
id: "homeserver",
r#type: "url",
placeholder: "https://matrix.org",
value: "{homeserver}",
oninput: move |evt| homeserver.set(evt.value()),
disabled: *is_loading.read(),
}
}
div {
class: "register-form__field",
label { r#for: "reg-username", "Username" }
input {
id: "reg-username",
r#type: "text",
placeholder: "Choose a username",
value: "{username}",
oninput: move |evt| username.set(evt.value()),
disabled: *is_loading.read(),
}
}
div {
class: "register-form__field",
label { r#for: "reg-password", "Password" }
input {
id: "reg-password",
r#type: "password",
placeholder: "Password (min 8 characters)",
value: "{password}",
oninput: move |evt| password.set(evt.value()),
disabled: *is_loading.read(),
}
}
div {
class: "register-form__field",
label { r#for: "reg-confirm", "Confirm Password" }
input {
id: "reg-confirm",
r#type: "password",
placeholder: "Confirm password",
value: "{confirm_password}",
oninput: move |evt| confirm_password.set(evt.value()),
disabled: *is_loading.read(),
}
}
button {
r#type: "submit",
class: "register-form__submit",
disabled: *is_loading.read(),
if *is_loading.read() {
"Creating account..."
} else {
"Create Account"
}
}
}
}
}
async fn register_account(
homeserver: &str,
username: &str,
password: &str,
) -> Result<matrix_sdk::Client, String> {
let matrix_client = client::build_client(homeserver)
.await
.map_err(|e| format!("Failed to connect to homeserver: {e}"))?;
use matrix_sdk::ruma::api::client::account::register;
let mut request = register::v3::Request::new();
request.username = Some(username.to_string());
request.password = Some(password.to_string());
request.initial_device_display_name = Some("Netrix".to_string());
match matrix_client.send(request).await {
Ok(response) => {
tracing::info!("Registered user: {:?}", response.user_id);
let client = client::login_with_password(homeserver, username, password)
.await
.map_err(|e| format!("Registered but failed to log in: {e}"))?;
Ok(client)
}
Err(e) => {
let err_str = e.to_string();
if err_str.contains("M_USER_IN_USE") {
Err("Username is already taken".to_string())
} else if err_str.contains("M_EXCLUSIVE") {
Err("This username is reserved".to_string())
} else if err_str.contains("M_FORBIDDEN") {
Err("Registration is disabled on this homeserver".to_string())
} else if err_str.contains("interactive auth") || err_str.contains("401") {
Err("This homeserver requires additional verification (CAPTCHA/email). Please register via the web.".to_string())
} else {
Err(format!("Registration failed: {e}"))
}
}
}
}