User Authentication and Other fixes #1
@@ -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,9 +381,13 @@ 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 {}
|
||||
|
||||
Reference in New Issue
Block a user