User Authentication and Other fixes #1

Merged
vinod merged 30 commits from user-auth into main 2023-02-05 09:01:08 +00:00
Showing only changes of commit fbd35658ec - Show all commits

View File

@@ -12,6 +12,7 @@ use wasm_bindgen::JsCast;
use web_sys::{Event, HtmlInputElement, KeyboardEvent}; // 0.3.5
use dotenv_codegen::dotenv;
use itertools::Itertools;
use std::collections::HashMap;
//use gloo_timers::future::TimeoutFuture;
//#[macro_use]
//extern crate dotenv_codegen;
@@ -50,12 +51,20 @@ struct PaginatedBookUIList {
books: Vec<BookUI>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AxumUser {
pub id: i32,
pub password_hash: String,
pub name: String,
}
#[derive(Debug, Default, Clone)]
pub struct AppState {
pub books: RcSignal<Vec<BookUI>>,
pub search: RcSignal<String>,
pub openlibrary: RcSignal<bool>,
pub internalsearch: RcSignal<bool>,
pub pagedisplay: RcSignal<u32>,
pub pagenum: RcSignal<u32>,
pub maxpage: RcSignal<u32>,
pub adding: RcSignal<bool>,
@@ -68,7 +77,12 @@ pub struct AppState {
pub updatingrequest: RcSignal<bool>,
pub addingrequest: RcSignal<bool>,
pub deleteid: RcSignal<i32>,
pub deleterequest: RcSignal<bool>
pub deleterequest: RcSignal<bool>,
pub userlist: RcSignal<HashMap<String, i32>>,
pub userid: RcSignal<i32>,
pub userscreen: RcSignal<bool>,
pub loggedin: RcSignal<bool>,
pub editmode: RcSignal<bool>,
}
#[derive(Route)]
@@ -81,6 +95,54 @@ enum AppRoutes {
NotFound,
}
/* TODO
let callback: Closure<dyn FnMut()> = Closure::new(move || {
info!("Got scroll event");
});
let window = web_sys::window().expect("Failed to get window");
window
.add_event_listener_with_callback_and_bool(
"scroll",
callback.as_ref().unchecked_ref(),
false, // Changing this to true does not help :/
)
.expect("Failed to set listener");
callback.forget();*/
async fn login_user(user: AxumUser) -> Result<reqwasm::http::Response, reqwasm::Error> {
let backend_url : &'static str = dotenv!("BACKEND_URL");
let url = format!("{}/api/login", backend_url);
let resp = Request::post(&url).body(serde_wasm_bindgen::to_value(&serde_json::to_string(&user).unwrap()).unwrap()).header("content-type","application/json").send().await?;
Ok(resp)
}
async fn register_user(user: AxumUser) -> Result<reqwasm::http::Response, reqwasm::Error> {
let backend_url : &'static str = dotenv!("BACKEND_URL");
let url = format!("{}/api/register", backend_url);
let resp = Request::post(&url).body(serde_wasm_bindgen::to_value(&serde_json::to_string(&user).unwrap()).unwrap()).header("content-type","application/json").send().await?;
Ok(resp)
}
async fn logout_user() -> Result<reqwasm::http::Response, reqwasm::Error> {
let backend_url : &'static str = dotenv!("BACKEND_URL");
let url = format!("{}/api/logout", backend_url);
let resp = Request::post(&url).send().await?;
Ok(resp)
}
async fn list_users() -> Result<HashMap<String,i32>, reqwasm::Error> {
let backend_url : &'static str = dotenv!("BACKEND_URL");
let url = format!("{}/api/list_users", backend_url);
let resp = Request::post(&url).send().await?;
let users = resp.json::<Vec<AxumUser>>().await?;
let mut res = HashMap::new();
for user in users {
res.insert(user.name, user.id);
}
Ok(res)
}
async fn fetch_books(search: String) -> Result<Vec<BookUI>, reqwasm::Error> {
//dotenvy::dotenv().ok();
let backend_url : &'static str = dotenv!("BACKEND_URL");
@@ -161,6 +223,7 @@ pub fn Header<G: Html>(cx: Scope) -> View<G> {
let value3 = create_signal(cx, String::new());
let input_ref3 = create_node_ref(cx);
let editchecked = create_signal(cx, false);
let handle_submit = |event: Event| {
let event: KeyboardEvent = event.unchecked_into();
@@ -211,32 +274,6 @@ pub fn Header<G: Html>(cx: Scope) -> View<G> {
};
let click_searchOL = |_| {
let mut task = value.get().as_ref().clone();
task = task.trim().to_string();
if !task.is_empty() {
app_state.search.set(task);
app_state.openlibrary.set(true);
app_state.internalsearch.set(false);
}
};
let click_searchall = |_| {
let mut task = value2.get().as_ref().clone();
task = task.trim().to_string();
if !task.is_empty() {
app_state.search.set(task);
app_state.openlibrary.set(false);
app_state.internalsearch.set(true);
app_state.refreshing.set(true);
}
};
let click_addbook = |_| {
app_state.adding.set(true);
app_state.updating.set(false);
@@ -273,28 +310,66 @@ pub fn Header<G: Html>(cx: Scope) -> View<G> {
});
}
};
let click_logout = |_| {
spawn_local(async move {
logout_user().await.unwrap();
});
};
let click_userscreen = |_| {
app_state.userscreen.set(true);
};
let toggle_editmode = |_| {
app_state.editmode.set(*editchecked.get());
};
view! { cx,
header(class="header") {
div(class="header-button"){
(if *app_state.loggedin.get() == false {
view!{ cx,
button(on:click=click_userscreen) { "Register/Login" }
}
} else {
view!{cx,
button(on:click=click_logout) { "Log Out" }
input(
class="toggle",
type="checkbox",
on:input=toggle_editmode,
bind:checked=editchecked
)
}
})
}
(if *app_state.loggedin.get() == false {
view!{ cx,
div(class="header-column"){
input(ref=input_ref,
class="new-todo",
placeholder="Search openlibrary",
placeholder="Search internet (openlibrary)",
bind:value=value,
on:keyup=handle_submit,
)
button(on:click=click_searchOL) { "Search OpenLibrary" }
}
} }
} else {
view!{cx, }
})
div(class="header-column"){
button(on:click=click_listall) { "List All DB" }
button(on:click=click_listall) { "All books" }
input(ref=input_ref2,
class="new-todo",
placeholder="Search internal DB",
placeholder="Search your library",
bind:value=value2,
on:keyup=handle_submit_seachall,
)
button(on:click=click_searchall) { "Search internal" }
}
(if *app_state.editmode.get() == false {
view!{ cx,
div(class="header-column"){
button(on:click=click_addbook) { "+ Add New" }
@@ -306,6 +381,10 @@ pub fn Header<G: Html>(cx: Scope) -> View<G> {
)
button(on:click=click_addbulk) { "Add bulk ISBNs" }
}
} } else {
view!{cx, ""}
})
div(class="header-page-column"){
PageBar{}
}
@@ -995,6 +1074,61 @@ async fn PageBar<G: Html>(cx: Scope<'_>) -> View<G> {
}
#[component]
async fn LoginScreenUI<G: Html>(cx: Scope<'_>) -> View<G> {
let app_state = use_context::<AppState>(cx);
let userval = create_signal(cx, String::new());
let input_refU = create_node_ref(cx);
let passval = create_signal(cx, String::new());
let input_refP = create_node_ref(cx);
let handle_register = move |_| {
let user = AxumUser{
id: 0,
name: (*userval.get()).clone(),
password_hash: (*passval.get()).clone(),
};
spawn_local(async move {
register_user(user)
.await.expect("Couldn't register user");
});
app_state.userscreen.set(false);
};
let handle_login = move |_| {
let user = AxumUser{
id: 0,
name: (*userval.get()).clone(),
password_hash: (*passval.get()).clone(),
};
spawn_local(async move {
login_user(user)
.await.expect("Couldn't login user");
});
app_state.userscreen.set(false);
};
view! {cx,
div {
(if *app_state.userscreen.get() == true {
view!{cx,
input(ref=input_refU,bind:value=userval)
input(ref=input_refP,bind:value=passval)
button(on:click=handle_login){ "LOGIN" }
button(on:click=handle_register){ "REGISTER" }
}
}else {
view!{cx,""}
})
}
}
}
#[component]
fn App<G: Html>(cx: Scope) -> View<G> {
let app_state = AppState {
@@ -1002,6 +1136,7 @@ fn App<G: Html>(cx: Scope) -> View<G> {
search: create_rc_signal(String::default()),
openlibrary: create_rc_signal(bool::default()),
internalsearch: create_rc_signal(bool::default()),
pagedisplay: create_rc_signal(1),
pagenum: create_rc_signal(1),
maxpage: create_rc_signal(1),
adding: create_rc_signal(bool::default()),
@@ -1015,6 +1150,11 @@ fn App<G: Html>(cx: Scope) -> View<G> {
addingrequest: create_rc_signal(bool::default()),
deleteid: create_rc_signal(-1),
deleterequest: create_rc_signal(bool::default()),
userlist: create_rc_signal(HashMap::new()),
userid: create_rc_signal(0),
userscreen: create_rc_signal(bool::default()),
loggedin: create_rc_signal(bool::default()),
editmode: create_rc_signal(bool::default()),
};
provide_context(cx, app_state);
view! {
@@ -1022,6 +1162,7 @@ fn App<G: Html>(cx: Scope) -> View<G> {
div {
Header {}
div(class="main"){
LoginScreenUI{}
AddingUI{}
SelectedUI{}
ListOL {}