dialtone_yew 0.1.0

Dialtone Yew Webapp
use crate::app_types::AppSiteConnection;
use crate::components::common::dt_button::*;
use crate::components::common::dt_input_password::*;
use crate::components::common::dt_input_text::*;
use crate::components::common::dt_status_const_text::*;
use crate::components::common::main_layout::*;
use crate::components::common::titled_panel::*;
use crate::components::pages::register::RegisterRoute;
use crate::utils::get_input_value::get_input_value;
use crate::utils::set_focus::set_focus;
use crate::MainRoute;
use bounce::use_atom;
use bounce::UseAtomHandle;
use dialtone_common::rest::users::user_exchanges::PostUser;
use dialtone_common::rest::users::user_exchanges::PostUserResponse;
use dialtone_common::rest::users::user_login::UserCredential;
use dialtone_common::utils::make_acct::make_acct;
use dialtone_reqwest::api_v1::names::check_name_available::check_name_available;
use dialtone_reqwest::api_v1::users::register_user::register_user;
use futures_util::stream::StreamExt;
use gloo::console::log;
use gloo_timers::future::IntervalStream;
use std::rc::Rc;
use yew::prelude::*;
use yew_router::prelude::*;

struct OpenRegistrationFormState {
    pub history: AnyHistory,
    pub disabled_register_button: UseStateHandle<bool>,
    pub in_progress: UseStateHandle<bool>,
    pub username: UseStateHandle<String>,
    pub password: UseStateHandle<String>,
    pub status: UseStateHandle<ConstText>,
    pub sc_atom: UseAtomHandle<AppSiteConnection>,
}

#[function_component(RegisterOpen)]
pub fn register_open() -> Html {
    const BLANK_STATUS: ConstText = ConstText::blank();
    const REGISTRATION_FAILED: ConstText =
        ConstText::new(ConstStatusType::WARNING, "Could not register account");
    const REGISTRATION_ERROR: ConstText =
        ConstText::new(ConstStatusType::ERROR, "Account registration unavailable");
    const NAME_UNAVAILABLE: ConstText =
        ConstText::new(ConstStatusType::WARNING, "User name is already in use.");

    let form_state = Rc::new(OpenRegistrationFormState {
        history: use_history().unwrap(),
        disabled_register_button: use_state(|| true),
        in_progress: use_state(|| false),
        username: use_state(|| "".to_string()),
        password: use_state(|| "".to_string()),
        status: use_state(|| BLANK_STATUS),
        sc_atom: use_atom::<AppSiteConnection>(),
    });

    let on_button_click = {
        let form_state = Rc::clone(&form_state);
        Callback::from(move |_| {
            let form_state = Rc::clone(&form_state);
            form_state.in_progress.set(true);
            form_state.status.set(BLANK_STATUS);
            wasm_bindgen_futures::spawn_local(async move {
                let sc = &form_state.sc_atom.0;
                let acct = make_acct(&form_state.username, &sc.host_name.as_ref().unwrap());
                let post_user = PostUser::OpenRegistration(UserCredential {
                    user_acct: acct,
                    password: form_state.password.to_string(),
                });
                let registration_result = register_user(sc, &post_user).await;
                match registration_result {
                    Ok(registration_response) => match registration_response {
                        PostUserResponse::AccountRegistered(_) => {
                            form_state.history.push(RegisterRoute::Success);
                        }
                        PostUserResponse::AccountNotRegistered(_) => {
                            form_state.status.set(REGISTRATION_FAILED);
                            form_state.in_progress.set(false);
                            set_focus("register_username");
                        }
                        PostUserResponse::AccountNeedsApproval(_) => {
                            form_state.history.push(RegisterRoute::ApprovalNeeded);
                        }
                    },
                    Err(_) => {
                        form_state.status.set(REGISTRATION_ERROR);
                        form_state.in_progress.set(false);
                    }
                }
            });
        })
    };

    let on_username = {
        let form_state = Rc::clone(&form_state);
        Callback::from(move |s| {
            form_state.username.set(s);
            if !form_state.username.is_empty() && !form_state.password.is_empty() {
                form_state.disabled_register_button.set(false);
            }
        })
    };

    let on_password = {
        let form_state = Rc::clone(&form_state);
        Callback::from(move |s| {
            form_state.password.set(s);
            if !form_state.username.is_empty() && !form_state.password.is_empty() {
                form_state.disabled_register_button.set(false);
            }
        })
    };

    use_effect_with_deps(
        move |_| {
            set_focus("register_username");
            || ()
        },
        (),
    );

    use_effect_with_deps(
        {
            let form_state = Rc::clone(&form_state);
            move |_| {
                wasm_bindgen_futures::spawn_local(async move {
                    let form_state = Rc::clone(&form_state);
                    let status = form_state.status.clone();
                    let sc_atom = form_state.sc_atom.clone();
                    IntervalStream::new(750)
                        .take_while(|_| async { get_input_value("register_username").is_some() })
                        .fold(String::new(), |name_to_check, _| async {
                            let status = status.clone();
                            let sc_atom = sc_atom.clone();
                            let input_value = get_input_value("register_username");
                            if input_value.is_none() {
                                name_to_check
                            } else {
                                let input_value = input_value.unwrap();
                                if input_value.len() > 2 && input_value != name_to_check {
                                    log!("checking name ", &input_value);
                                    let name_available =
                                        check_name_available(&sc_atom.0, &input_value).await;
                                    if let Ok(name_available) = name_available {
                                        if !name_available {
                                            status.set(NAME_UNAVAILABLE);
                                        } else {
                                            status.set(BLANK_STATUS);
                                        }
                                    }
                                    input_value
                                } else {
                                    name_to_check
                                }
                            }
                        })
                        .await;
                });
                || ()
            }
        },
        (),
    );

    html! {
        <MainLayout>
            <div class="centered_form_page">
                <TitledPanel title="Register Account">
                    <form class="basic_form"
                        onsubmit={Callback::from(|e: FocusEvent| e.prevent_default())}
                        autocomplete="off" >
                        <label for="register_username">{"Username:"}</label>
                        <DtInputText
                            id="register_username"
                            disabled=false
                            name="register_username"
                            on_update_value={on_username}/>
                        <label for="register_password">{"Password:"}</label>
                        <DtInputPassword
                            id="register_password"
                            disabled=false
                            name="register_password"
                            on_update_value={on_password}/>
                        <DtButton
                            text="Register"
                            disabled={*form_state.disabled_register_button}
                            on_click={on_button_click.clone()}
                            id="register_button"
                            in_progress={*form_state.in_progress}/>
                        <DtStatusConstText
                            status={(*form_state.status).clone()} />
                        <span>
                            <Link<MainRoute>
                                classes={classes!("in_page_action")}
                                to={MainRoute::Login}>{"Login to Existing Account"}
                            </Link<MainRoute>></span>
                    </form>
                </TitledPanel>
            </div>
        </MainLayout>
    }
}