v0.15-WORKINGV1

This commit is contained in:
2022-12-03 22:43:49 +05:30
parent c7c2465a87
commit 9f7731ef42
12 changed files with 504 additions and 147 deletions

View File

@@ -1,4 +1,5 @@
use log::info;
//use std::env;
//use log::Level;
use reqwasm::http::Request;
use serde::{Deserialize, Serialize};
@@ -9,6 +10,10 @@ use sycamore::prelude::*;
use sycamore_router::Route;
use wasm_bindgen::JsCast;
use web_sys::{Event, HtmlInputElement, KeyboardEvent}; // 0.3.5
use dotenv_codegen::dotenv;
use itertools::Itertools;
//#[macro_use]
//extern crate dotenv_codegen;
#[derive(Deserialize, Serialize, Debug, Default, Clone, PartialEq, Hash, Eq)]
pub struct BookUI {
@@ -38,7 +43,7 @@ pub struct BookUIProp {
bookitem: BookUI,
}
#[derive(Deserialize, Serialize, Debug)]
#[derive(Deserialize, Serialize, Debug, Clone)]
struct PaginatedBookUIList {
num_pages: u32,
books: Vec<BookUI>,
@@ -51,11 +56,13 @@ pub struct AppState {
pub openlibrary: RcSignal<bool>,
pub internalsearch: RcSignal<bool>,
pub pagenum: RcSignal<u32>,
pub maxpage: RcSignal<u32>,
pub adding: RcSignal<bool>,
pub updating: RcSignal<bool>,
pub displaying: RcSignal<bool>,
pub addingbook: RcSignal<BookUI>,
pub displayingbook: RcSignal<BookUI>,
pub refreshing: RcSignal<bool>,
}
#[derive(Route)]
@@ -69,8 +76,8 @@ enum AppRoutes {
}
async fn fetch_books(search: String) -> Result<Vec<BookUI>, reqwasm::Error> {
dotenvy::dotenv().ok();
let backend_url = "http://localhost:8081";
//dotenvy::dotenv().ok();
let backend_url : &'static str = dotenv!("BACKEND_URL");
//env::var("BACKEND_URL").expect("BACKEND_URL is not set in .env file");
let url = format!("{}/api/search_openlibrary?search={}", backend_url, search);
let resp = Request::get(&url).send().await?;
@@ -80,8 +87,8 @@ async fn fetch_books(search: String) -> Result<Vec<BookUI>, reqwasm::Error> {
}
async fn search_books(search: String, page: u32) -> Result<PaginatedBookUIList, reqwasm::Error> {
dotenvy::dotenv().ok();
let backend_url = "http://localhost:8081";
let backend_url : &'static str = dotenv!("BACKEND_URL");
//"http://localhost:8081";
//env::var("BACKEND_URL").expect("BACKEND_URL is not set in .env file");
let url = format!("{}/api/list_search?search={}&page={}",backend_url, search, page);
let resp = Request::get(&url).send().await?;
@@ -91,8 +98,7 @@ async fn search_books(search: String, page: u32) -> Result<PaginatedBookUIList,
}
async fn add_book(record: BookUI) -> Result<reqwasm::http::Response, reqwasm::Error> {
dotenvy::dotenv().ok();
let backend_url = "http://localhost:8081";
let backend_url : &'static str = dotenv!("BACKEND_URL");
//env::var("BACKEND_URL").expect("BACKEND_URL is not set in .env file");
let url = format!("{}/api/create", backend_url);
let resp = Request::post(&url).body(serde_wasm_bindgen::to_value(&serde_json::to_string(&record).unwrap()).unwrap()).header("content-type","application/json").send().await?;
@@ -100,8 +106,7 @@ async fn add_book(record: BookUI) -> Result<reqwasm::http::Response, reqwasm::Er
}
async fn add_books_isbns(isbns: String) -> Result<reqwasm::http::Response, reqwasm::Error> {
dotenvy::dotenv().ok();
let backend_url = "http://localhost:8081";
let backend_url : &'static str = dotenv!("BACKEND_URL");
//env::var("BACKEND_URL").expect("BACKEND_URL is not set in .env file");
let url = format!("{}/api/create_by_isbn?isbns={}", backend_url, isbns);
let resp = Request::get(&url).send().await?;
@@ -111,8 +116,7 @@ async fn add_books_isbns(isbns: String) -> Result<reqwasm::http::Response, reqw
async fn update_book(record: BookUI) -> Result<reqwasm::http::Response, reqwasm::Error> {
dotenvy::dotenv().ok();
let backend_url = "http://localhost:8081";
let backend_url : &'static str = dotenv!("BACKEND_URL");
//env::var("BACKEND_URL").expect("BACKEND_URL is not set in .env file");
let url = format!("{}/api/update", backend_url);
let resp = Request::post(&url).body(serde_wasm_bindgen::to_value(&serde_json::to_string(&record).unwrap()).unwrap()).header("content-type","application/json").send().await?;
@@ -120,8 +124,7 @@ async fn update_book(record: BookUI) -> Result<reqwasm::http::Response, reqwasm:
}
async fn delete_book(id: i32) -> Result<reqwasm::http::Response, reqwasm::Error> {
dotenvy::dotenv().ok();
let backend_url = "http://localhost:8081";
let backend_url : &'static str = dotenv!("BACKEND_URL");
//env::var("BACKEND_URL").expect("BACKEND_URL is not set in .env file");
let url = format!("{}/api/delete/{}", backend_url, id);
let resp = Request::get(&url).send().await?;
@@ -129,8 +132,7 @@ async fn delete_book(id: i32) -> Result<reqwasm::http::Response, reqwasm::Error>
}
async fn list_books(page: u32) -> Result<PaginatedBookUIList, reqwasm::Error> {
dotenvy::dotenv().ok();
let backend_url = "http://localhost:8081";
let backend_url : &'static str = dotenv!("BACKEND_URL");
//env::var("BACKEND_URL").expect("BACKEND_URL is not set in .env file");
let url = format!("{}/api/list?page={}", backend_url, page);
let resp = Request::get(&url).send().await?;
@@ -290,6 +292,9 @@ pub fn Header<G: Html>(cx: Scope) -> View<G> {
)
button(on:click=click_addbulk) { "Add bulk ISBNs" }
}
div(class="header-page-column"){
PageBar{}
}
}
}
}
@@ -304,6 +309,8 @@ async fn ListDB<G: Html>(cx: Scope<'_>) -> View<G> {
app_state.pagenum.track();
app_state.openlibrary.track();
app_state.internalsearch.track();
app_state.refreshing.track();
app_state.pagenum.track();
info!(
"The state changed. New value: {}",
@@ -311,34 +318,45 @@ async fn ListDB<G: Html>(cx: Scope<'_>) -> View<G> {
);
if *app_state.openlibrary.get() == false {
app_state.refreshing.set(false);
if *app_state.internalsearch.get() == false {
spawn_local(async move {
let res = list_books(*app_state.pagenum.get())
.await.unwrap();
app_state.books.set(
list_books(*app_state.pagenum.get())
.await
.unwrap().books
res.books
);
app_state.maxpage.set(
res.num_pages
)
});
} else {
spawn_local(async move {
let res = search_books(app_state.search.get().to_string(), *app_state.pagenum.get())
.await.unwrap();
app_state.books.set(
search_books(app_state.search.get().to_string(), *app_state.pagenum.get())
.await
.unwrap().books)
})
res.books
);
app_state.maxpage.set(
res.num_pages
)
});
}
} else {
spawn_local(async move {
if *app_state.search.get() != "" {
if *app_state.openlibrary.get() == true {
app_state.books.set(
fetch_books(app_state.search.get().to_string() )
.await
.unwrap(),
)
if *app_state.refreshing.get() == false {
spawn_local(async move {
if *app_state.search.get() != "" {
if *app_state.openlibrary.get() == true {
app_state.books.set(
fetch_books(app_state.search.get().to_string() )
.await
.unwrap(),
)
}
}
}
});
});
}
}
});
@@ -366,7 +384,7 @@ async fn ListDB<G: Html>(cx: Scope<'_>) -> View<G> {
#[component(inline_props)]
pub fn BookDB<G: Html>(cx: Scope, bookitem: BookUIProp) -> View<G> {
let app_state = use_context::<AppState>(cx);
let app_state = use_context::<AppState>(cx);
let bookdelete = bookitem.bookitem.clone();
let bookupdate = bookitem.bookitem.clone();
let bookdisplay = bookitem.bookitem.clone();
@@ -378,15 +396,18 @@ pub fn BookDB<G: Html>(cx: Scope, bookitem: BookUIProp) -> View<G> {
let coverurl = bookdisplay.clone().cover.clone().unwrap_or("None".to_string());
let handle_delete = move |_| {
spawn_local(async move {
let temp = delete_book(bookdelete.id).await.unwrap();
let temp = delete_book(bookdelete.id).await.unwrap();
println!("{}",temp.status());
});
app_state.refreshing.set(true);
};
let handle_update = move |_| {
app_state.adding.set(false);
app_state.updating.set(true);
app_state.addingbook.set(bookupdate.clone());
app_state.refreshing.set(true);
};
let handle_display = move |_| {
@@ -398,19 +419,29 @@ pub fn BookDB<G: Html>(cx: Scope, bookitem: BookUIProp) -> View<G> {
view! { cx,
div(class="column"){
div(class="card"){
div(class="card-buttons"){
button(class="delete", on:click=handle_delete){ "DEL-" }
button(class="update", on:click=handle_update){ "EDIT=" }
button(class="info", on:click=handle_display){ "INFO+" }
}
div(class="card-main"){
div(class="card-title"){
(format!("{}",loctitle))
}
div(class="card-authors"){
(format!("{}",locauthors))
}
//div(class="card-image"){
img(src=coverurl,width="100")
(format!("{:?}",loctitle))
br{}
(format!("{:?}",locauthors))
br{}
(format!("{:?}",locdesc))
//}
}
div(class="card-desc"){
(format!("{}",locdesc))
}
}
}
@@ -460,7 +491,6 @@ pub fn BookOL<G: Html>(cx: Scope, bookitem: BookUIProp) -> View<G> {
let bookdisp=book.clone();
let loctitle = bookitem.bookitem.clone().title.clone();
let locauthors = bookitem.bookitem.clone().author_name.clone().unwrap_or(vec!["".to_string()]).join(", ");
let locdesc = bookitem.bookitem.clone().description.unwrap_or("".to_string());
let coverurl = bookdisp.cover.clone().unwrap_or("NONE".to_string());
let app_state = use_context::<AppState>(cx);
let handle_add = move |_| {
@@ -470,15 +500,20 @@ pub fn BookOL<G: Html>(cx: Scope, bookitem: BookUIProp) -> View<G> {
view! { cx,
div(class="column"){
div(class="card"){
img(src=coverurl,width="100")
div(class="card-openlibrary"){
button(class="add", on:click=handle_add){ "ADD+" }
div(class="card-main"){
(format!("{:?}",loctitle))
br{}
(format!("{:?}",locauthors))
br{}
(format!("{:?}",locdesc))
button(class="add", on:click=handle_add){ "+" }
div(class="card-title"){
(format!("{}",loctitle))
}
div(class="card-authors"){
(format!("{}",locauthors))
}
//div(class="card-image"){
img(src=coverurl,width="100")
//}
}
}
}
}
@@ -587,6 +622,7 @@ info!("Adding book");
app_state.addingbook.set(BookUI::default());
app_state.updating.set(false);
app_state.adding.set(false);
app_state.refreshing.set(true);
};
@@ -613,27 +649,80 @@ info!("Adding book");
(if *app_state.adding.get() == true || *app_state.updating.get() == true {
view!{ cx,
div(class="modal-content"){
p {
input(bind:value=inp_title, placeholder="Title" )
input(bind:value=inp_olkey, placeholder="OpenLibrary Key" )
input(bind:value=inp_editioncount, placeholder="Number of editions" )
input(bind:value=inp_publishyear, placeholder="First publish year" )
input(bind:value=inp_medianpage, placeholder="Page count" )
input(bind:value=inp_goodread, placeholder="GoodRead ID" )
input(bind:value=inp_desc, placeholder="Description" )
input(bind:value=inp_cover, placeholder="Cover URL" )
input(bind:value=inp_location, placeholder="Location" )
input(bind:value=inp_rating, placeholder="Rating (/10)" )
input(bind:value=inp_comments, placeholder="Comments" )
input(bind:value=inp_author, placeholder="Authors")
input(bind:value=inp_person, placeholder="Persons" )
input(bind:value=inp_place, placeholder="Places" )
input(bind:value=inp_subject, placeholder="Subjects" )
input(bind:value=inp_time, placeholder="Times" )
input(bind:value=inp_isbn, placeholder="ISBNs" )
button(class="add", on:click=handle_add){ "Add/Update book to DB" }
div(class="input-buttons"){
button(class="add", on:click=handle_add){ "Add/Update book to DB" }
button(class="cancel", on:click=handle_cancel){ "Cancel" }
}
p {
div(class="input-field"){
label{"Title"}
textarea(bind:value=inp_title, placeholder="Title" )
}
div(class="input-field"){
label{"OpenLib Key"}
textarea(bind:value=inp_olkey, placeholder="OpenLibrary Key" )
}
div(class="input-field"){
label{"Editions"}
textarea(bind:value=inp_editioncount, placeholder="Number of editions" )
}
div(class="input-field"){
label{"Publish year"}
textarea(bind:value=inp_publishyear, placeholder="First publish year" )
}
div(class="input-field"){
label{"Pages"}
textarea(bind:value=inp_medianpage, placeholder="Page count" )
}
div(class="input-field"){
label{"GoodreadID"}
textarea(bind:value=inp_goodread, placeholder="GoodRead ID" )
}
div(class="input-field"){
label{"Description"}
textarea(bind:value=inp_desc, placeholder="Description" )
}
div(class="input-field"){
label{"Cover"}
textarea(bind:value=inp_cover, placeholder="Cover URL" )
}
div(class="input-field"){
label{"Location"}
textarea(bind:value=inp_location, placeholder="Location" )
}
div(class="input-field"){
label{"Rating"}
textarea(bind:value=inp_rating, placeholder="Rating (/10)" )
}
div(class="input-field"){
label{"Comments"}
textarea(bind:value=inp_comments, placeholder="Comments" )
}
div(class="input-field"){
label{"Authors"}
textarea(bind:value=inp_author, placeholder="Authors")
}
div(class="input-field"){
label{"Persons"}
textarea(bind:value=inp_person, placeholder="Persons" )
}
div(class="input-field"){
label{"Places"}
textarea(bind:value=inp_place, placeholder="Places" )
}
div(class="input-field"){
label{"Subjects"}
textarea(bind:value=inp_subject, placeholder="Subjects" )
}
div(class="input-field"){
label{"Times"}
textarea(bind:value=inp_time, placeholder="Times" )
}
div(class="input-field"){
label{"ISBNs"}
textarea(bind:value=inp_isbn, placeholder="ISBNs" )
}
}
}
@@ -653,12 +742,43 @@ info!("Adding book");
#[component]
async fn SelectedUI<G: Html>(cx: Scope<'_>) -> View<G> {
let app_state = use_context::<AppState>(cx);
let displ_book = create_memo(cx, || app_state.displayingbook.get());
let displaybook = create_memo(cx, || app_state.displayingbook.get());
let coverurl = create_memo(cx, || app_state.displayingbook.get().clone().cover.clone().unwrap_or("NONE".to_string()).to_string().clone());
let node_ref = create_node_ref(cx);
//on_mount(cx, || {
// let dom_node = node_ref.get::<DomNode>();
//});
let dtitle = create_memo(cx, || app_state.displayingbook.get().title.clone());
let dollink = create_memo(cx, || {
let olkey = app_state.displayingbook.get().open_library_key.clone().unwrap_or("".to_string());
format!("https://openlibrary.org{}",olkey)
}
);
let deditions = create_memo(cx, || app_state.displayingbook.get().edition_count.unwrap_or(0) );
let dpubyr = create_memo(cx, || app_state.displayingbook.get().first_publish_year.unwrap_or(0) );
let dpages = create_memo(cx, || app_state.displayingbook.get().median_page_count.unwrap_or(0) );
let dgoodread = create_memo(cx, || {
let goodreadid = app_state.displayingbook.get().goodread_id.clone().unwrap_or("".to_string());
format!("https://goodreads.com/en/book/show/{}",goodreadid)
}
);
let ddesc = create_memo(cx, || app_state.displayingbook.get().description.clone().unwrap_or("".to_string()) );
let dlocation = create_memo(cx, || app_state.displayingbook.get().location.clone().unwrap_or("".to_string()) );
let dcomments = create_memo(cx, || app_state.displayingbook.get().comments.clone().unwrap_or("".to_string()) );
let dtags = create_memo(cx, || {
let persons = app_state.displayingbook.get().person.clone().unwrap_or(vec!["".to_string()]).iter().filter(|word| !word.is_empty()).map(|s| format!("#{}",s.clone())).collect::<Vec<String>>().join(", ");
let places = app_state.displayingbook.get().place.clone().unwrap_or(vec!["".to_string()]).iter().filter(|word| !word.is_empty()).map(|s| format!("#{}",s.clone())).collect::<Vec<String>>().join(", ");
let subjects = app_state.displayingbook.get().subject.clone().unwrap_or(vec!["".to_string()]).iter().filter(|word| !word.is_empty()).map(|s| format!("#{}",s.clone())).collect::<Vec<String>>().join(", ");
let times = app_state.displayingbook.get().time.clone().unwrap_or(vec!["".to_string()]).iter().filter(|word| !word.is_empty()).map(|s| format!("#{}",s.clone())).collect::<Vec<String>>().join(", ");
let alltags = vec![persons,places,subjects,times].iter().filter(|word| !word.is_empty()).join(", ");
format!("{}",alltags)
}
);
let disbn = create_memo(cx, || {
app_state.displayingbook.get().isbn.clone().unwrap_or(vec!["".to_string()])[0].clone()
});
let dauthors = create_memo(cx, || {
app_state.displayingbook.get().author_name.clone().unwrap_or(vec!["".to_string()]).join(", ")
});
let handle_close = move |_| {
app_state.displaying.set(false);
@@ -669,6 +789,17 @@ async fn SelectedUI<G: Html>(cx: Scope<'_>) -> View<G> {
}
};
let handle_edit = move |_| {
app_state.displaying.set(false);
let dom_node = node_ref.try_get::<DomNode>();
if dom_node.is_some() {
dom_node.unwrap().set_attribute("popup-display","false");
}
app_state.addingbook.set((*(*displaybook.get())).clone());
app_state.updating.set(true);
};
create_effect(cx, || {
if *app_state.displaying.get() == true {
@@ -692,13 +823,50 @@ async fn SelectedUI<G: Html>(cx: Scope<'_>) -> View<G> {
view!{ cx,
div(class="modal-content"){
p{
div(class="select-book"){
div(class="more-info"){
div(class="more-info-buttons"){
button(class="close", on:click=handle_close){ "CLOSE" }
img(src=coverurl.get(),width="200")
(format!("{:?}",displ_book.get()))
button(class="close", on:click=handle_edit){ "EDIT" }
}
}
img(src=coverurl.get(),width="200")
label{"Title: "}
(format!("{}",dtitle.get()))
br{}
label{"Authors: "}
(format!("{}",dauthors.get()))
br{}
label{"Location: "}
(format!("{}",dlocation.get()))
br{}
label{"Editions: "}
(format!("{}",deditions.get()))
br{}
label{"Publish Year: "}
(format!("{}",dpubyr.get()))
br{}
label{"Page Count: "}
(format!("{}",dpages.get()))
br{}
label{"Openlibrary Link: "}
(format!("{}",dollink.get()))
br{}
label{"ISBN: "}
(format!("{}",disbn.get()))
br{}
label{"GoodReads Link: "}
(format!("{}",dgoodread.get()))
br{}
label{"Description: "}
(format!("{}",ddesc.get()))
br{}
label{"Comments: "}
(format!("{}",dcomments.get()))
br{}
label{"Tags: "}
(format!("{}",dtags.get()))
br{}
}
}
}}
else {
@@ -713,22 +881,48 @@ async fn SelectedUI<G: Html>(cx: Scope<'_>) -> View<G> {
#[component]
async fn PageBar<G: Html>(cx: Scope<'_>) -> View<G> {
let app_state = use_context::<AppState>(cx);
let currpg = create_signal(cx, (*app_state.pagenum.get()).to_string());
let input_ref = create_node_ref(cx);
let handle_add = move |_| {
app_state.pagenum.set(*app_state.pagenum.get()+1);
};
let handle_sub = move |_| {
app_state.pagenum.set(*app_state.pagenum.get()-1);
if *app_state.pagenum.get()>1 {
app_state.pagenum.set(*app_state.pagenum.get()-1);
}
};
let handle_submit = |event: Event| {
let event: KeyboardEvent = event.unchecked_into();
if event.key() == "Enter" {
let pg = currpg.get().as_ref().clone().parse::<u32>().unwrap_or(1);
if pg>0 && pg<*app_state.maxpage.get() {
app_state.pagenum.set(pg);
} else {
currpg.set((*app_state.pagenum.get()).to_string());
}
}
};
view! {cx,
p {
div {
(if *app_state.openlibrary.get() == false || *app_state.internalsearch.get() == true {
view!{ cx,
button(class="page", on:click=handle_sub){ "Page -" }
(format!("{:?}",app_state.pagenum.get()))
button(class="page", on:click=handle_add){ "Page +" }
label{"PAGE "}
input(ref=input_ref,bind:value=currpg,on:keyup=handle_submit,class="page-input")
(if *app_state.pagenum.get()>1 {
view!{cx, button(class="page", on:click=handle_sub){ "-" }}
} else {
view!{cx,""}
})
(if *app_state.pagenum.get()<*app_state.maxpage.get() {
view!{cx, button(class="page", on:click=handle_add){ "+" }}
} else {
view!{cx,""}
})
}
}else {
view!{cx,""}
@@ -747,22 +941,26 @@ fn App<G: Html>(cx: Scope) -> View<G> {
openlibrary: create_rc_signal(bool::default()),
internalsearch: create_rc_signal(bool::default()),
pagenum: create_rc_signal(1),
maxpage: create_rc_signal(1),
adding: create_rc_signal(bool::default()),
updating: create_rc_signal(bool::default()),
addingbook: create_rc_signal(BookUI::default()),
displaying: create_rc_signal(bool::default()),
displayingbook: create_rc_signal(BookUI::default()),
refreshing: create_rc_signal(bool::default()),
};
provide_context(cx, app_state);
view! {
cx,
div {
Header {}
div(class="main"){
AddingUI{}
SelectedUI{}
ListOL {}
ListDB{}
PageBar{}
}
//PageBar{}
}
}
}